menacher / java-game-server

Jetserver is a high speed nio socket based multiplayer java game server written using Netty and Mike Rettig's Jetlang.It is specifically tuned for network based multiplayer games and supports TCP and UDP network protocols.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

onException/onDisconnect/onClose are not delivered to DefaultSessionEventHandler

nikolaykuz opened this issue · comments

I have following scenario. There are 3 players in the game room, so there are 3 SessionHandlers. I shutdown one of the players. So it is SessionHandler detects that and prints onException. Then I have a code inside ServerGameManager that sends a NETWORK_MESSAGE to other clients of the same game to shutdown also.
But for two other clients handlers of their SessionHandlers are never called.

public class SessionHandler extends DefaultSessionEventHandler {

    //example

    @Override
    protected void onException(Event event) {
         System.out.println("onException");
    } 

    @Override
    protected void onDisconnect(Event event) {
         System.out.println("onDisconnect");
    } 

    @Override
    protected void onClose(Event event) {
         System.out.println("onClose");
    } 
2013-04-23 15:22:07,841 [DefaultToServerHandler.java:49][ERROR]:Exception in DefaultToServerHandler class: [id: 0x03ecf7ad, /10.254.215.116:60277 => /10.254.215.116:81] EXCEPTION: java.io.IOException: An existing connection was forcibly closed by the remote host.
2013-04-23 15:22:07,842 [DefaultToServerHandler.java:60][DEBUG]:Channel 65861549 is disconnected, raising the event to session
2013-04-23 15:22:07,842 [SessionHandler.java:60][ERROR]:GAMEROOM1 PLAYER[0] onException
2013-04-23 15:22:07,842 [DefaultToServerHandler.java:78][DEBUG]:Channel 65861549 is closed and resources released
2013-04-23 15:22:07,842 [NettyTCPMessageSender.java:57][INFO]:Going to close tcp connection in class: org.menacheri.jetserver.communication.NettyTCPMessageSender
2013-04-23 15:22:07,842 [NettyTCPMessageSender.java:66][WARN]:Unable to write the Event Event [type=34, source=null, timeStamp=1366698127842] with type 34 to socket
2013-04-23 15:22:07,843 [JetlangEventDispatcher.java:260][TRACE]:Entered removeHandlersForSession for session PlayerSession [id=1player=org.menacheri.jetserver.app.impl.DefaultPlayer@d5904c89, parentGameRoom=org.evogame.server.RealGameRoom@50, protocol=null, isShuttingDown=true]
2013-04-23 15:22:07,843 [JetlangEventDispatcher.java:272][TRACE]:Going to remove 1 handlers for session: PlayerSession [id=1player=org.menacheri.jetserver.app.impl.DefaultPlayer@d5904c89, parentGameRoom=org.evogame.server.RealGameRoom@50, protocol=null, isShuttingDown=true]
2013-04-23 15:22:07,843 [JetlangEventDispatcher.java:284][TRACE]:Going to clear handlers on dispatcher org.menacheri.jetserver.event.impl.JetlangEventDispatcher@925c95
2013-04-23 15:22:07,843 [ServerGameManager.java:145][INFO]:GAMEROOM1 PLAYER[0] has disconnected!
2013-04-23 15:22:07,854 [DefaultToServerHandler.java:60][DEBUG]:Channel 476923117 is disconnected, raising the event to session
2013-04-23 15:22:07,854 [DefaultToServerHandler.java:78][DEBUG]:Channel 476923117 is closed and resources released
2013-04-23 15:22:07,854 [DefaultSessionEventHandler.java:179][DEBUG]:Received disconnect event in session. Going to close session
2013-04-23 15:22:07,854 [NettyTCPMessageSender.java:57][INFO]:Going to close tcp connection in class: org.menacheri.jetserver.communication.NettyTCPMessageSender
2013-04-23 15:22:07,854 [NettyTCPMessageSender.java:66][WARN]:Unable to write the Event Event [type=34, source=null, timeStamp=1366698127854] with type 34 to socket
2013-04-23 15:22:07,855 [JetlangEventDispatcher.java:260][TRACE]:Entered removeHandlersForSession for session PlayerSession [id=2player=org.menacheri.jetserver.app.impl.DefaultPlayer@3c452253, parentGameRoom=org.evogame.server.RealGameRoom@50, protocol=null, isShuttingDown=true]
2013-04-23 15:22:07,855 [JetlangEventDispatcher.java:272][TRACE]:Going to remove 1 handlers for session: PlayerSession [id=2player=org.menacheri.jetserver.app.impl.DefaultPlayer@3c452253, parentGameRoom=org.evogame.server.RealGameRoom@50, protocol=null, isShuttingDown=true]
2013-04-23 15:22:07,855 [JetlangEventDispatcher.java:284][TRACE]:Going to clear handlers on dispatcher org.menacheri.jetserver.event.impl.JetlangEventDispatcher@18309b0
2013-04-23 15:22:07,855 [DefaultSessionEventHandler.java:179][DEBUG]:Received disconnect event in session. Going to close session
2013-04-23 15:22:07,856 [DefaultToServerHandler.java:60][DEBUG]:Channel -395629090 is disconnected, raising the event to session
2013-04-23 15:22:07,856 [DefaultToServerHandler.java:78][DEBUG]:Channel -395629090 is closed and resources released
2013-04-23 15:22:07,856 [DefaultSessionEventHandler.java:179][DEBUG]:Received disconnect event in session. Going to close session
2013-04-23 15:22:07,856 [NettyTCPMessageSender.java:57][INFO]:Going to close tcp connection in class: org.menacheri.jetserver.communication.NettyTCPMessageSender
2013-04-23 15:22:07,856 [NettyTCPMessageSender.java:66][WARN]:Unable to write the Event Event [type=34, source=null, timeStamp=1366698127856] with type 34 to socket
2013-04-23 15:22:07,856 [JetlangEventDispatcher.java:260][TRACE]:Entered removeHandlersForSession for session PlayerSession [id=3player=org.menacheri.jetserver.app.impl.DefaultPlayer@4536c9ea, parentGameRoom=org.evogame.server.RealGameRoom@50, protocol=null, isShuttingDown=true]
2013-04-23 15:22:07,857 [JetlangEventDispatcher.java:272][TRACE]:Going to remove 1 handlers for session: PlayerSession [id=3player=org.menacheri.jetserver.app.impl.DefaultPlayer@4536c9ea, parentGameRoom=org.evogame.server.RealGameRoom@50, protocol=null, isShuttingDown=true]
2013-04-23 15:22:07,857 [JetlangEventDispatcher.java:284][TRACE]:Going to clear handlers on dispatcher org.menacheri.jetserver.event.impl.JetlangEventDispatcher@c6824e
2013-04-23 15:22:07,857 [DefaultSessionEventHandler.java:179][DEBUG]:Received disconnect event in session. Going to close session

Could you provide the code you have in ServerGameManager that will disconnect the other 2 sessions. I will look into this in parallel.

Channel.close();
System.exit(0);

combinations of both

any reason why you dont use playerSession.close() instead?

at client side I don't have playerSession. I mean ServerGameManager sends some message to clients, upon reception they invoke

Channel.close();
System.exit(0);

@nikolaykuz I haven't had time to look at this, will do so later today/tomorrow and let you know.

Hopefully, in this week :)

@nikolaykuz I wrote a unit test and it seems to work as expected. The subclass is getting the events. Even in your logs, I can see that the disconnect session event is reach the super class DefaultSessionEventDispatcher.

    @Test
    public void multiSessionDisconnectValidation() throws InterruptedException {
        // create necessary setup objects.
        Game game = new SimpleGame(1, "Test");
        Protocol dummyProtocol = new DummyProtocol();
        GameRoomSessionBuilder sessionBuilder = new GameRoomSessionBuilder();
        sessionBuilder.parentGame(game).gameRoomName("Zombie_ROOM_1")
                .protocol(dummyProtocol);
        CountDownLatch latch1 = new CountDownLatch(2);
        CountDownLatch latch2 = new CountDownLatch(2);
        AtomicLong counter = new AtomicLong(0l);
        Session gameRoomSession = new TestGameRoom(sessionBuilder, counter,
                latch1);
        GameRoom gameRoom = (GameRoom) gameRoomSession;
        PlayerSession playerSession = gameRoom.createPlayerSession(null);
        PlayerSession playerSession2 = gameRoom.createPlayerSession(null);
        PlayerSession playerSession3 = gameRoom.createPlayerSession(null);
        gameRoom.connectSession(playerSession);
        gameRoom.connectSession(playerSession2);
        gameRoom.connectSession(playerSession3);
        playerSession.addHandler(new SessionHandlerLatchCounter(playerSession,
                counter, latch1));
        playerSession2.addHandler(new SessionHandlerLatchCounter(playerSession2,
                counter, latch2));
        playerSession3.addHandler(new SessionHandlerLatchCounter(playerSession3,
                counter, latch2));
        // start test
        Event event1 = Events.event(null, Events.DISCONNECT);
        playerSession.onEvent(event1);// disconnect session 1.
        assertFalse(latch1.await(1000, TimeUnit.MILLISECONDS));// This is just a wait
        Event message = Events.event(null, Events.SESSION_MESSAGE);
        playerSession.onEvent(message);
        assertFalse(latch1.await(500, TimeUnit.MILLISECONDS));// Ensure that the message is not sent.
        Event event2 = Events.event(null, Events.DISCONNECT);
        Event event3 = Events.event(null, Events.DISCONNECT);
        playerSession2.onEvent(event2);
        playerSession3.onEvent(event3);

        assertTrue(latch2.await(500, TimeUnit.MILLISECONDS));
        // 1 ondisconnect(session1) + 0 onnetwork(session1) + 2 ondisconnect(session2 and 3)
        assertTrue(counter.get() == 3);
    }

I overloaded onDisconnect of class SessionHandlerLatchCounter which extends from DefaultSessionEventHandler and it received the events without issue. As you can see final assert is for counter to be 3 I couldn't try out an exact replica of your test, I will try to do that and let you know, but at first look there does not seem to be a bug.
Can you put break point in both DefaultSessionEventHandler and your SessionHandler and see why disconnect is getting invoked only on the super class and not on subclass. You will generally be able to understand by looking at which method is invoking this onDisconnect method.

b.t.w this is the relevant code from SessionHandlerLatchCounter

    @Override
    public void onNetworkMessage(NetworkEvent event) {
        counter.incrementAndGet();
        latch.countDown();
        System.out.println("invoked onNetworkMessage");
    }

    @Override
    protected void onDisconnect(Event event)
    {
        counter.incrementAndGet();
        latch.countDown();
        System.out.println("invoked onDisconnect");
        super.onDisconnect(event);
    }

Hello @menacher, merge please SessionHandlerLatchCounter changes to master branch. It fails multiSessionDisconnectValidation test as of now.

Hi @tgregory thanks for pointing it out. I will commit this tonight. Here is the relevant code from netty4 branch(I committed in this branch and forgot to commit to master)

package org.menacheri.jetserver.util;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;

import org.menacheri.jetserver.app.Session;
import org.menacheri.jetserver.event.Event;
import org.menacheri.jetserver.event.NetworkEvent;
import org.menacheri.jetserver.event.impl.DefaultSessionEventHandler;

public class SessionHandlerLatchCounter extends DefaultSessionEventHandler {

    private final AtomicLong counter;
    private final CountDownLatch latch;

    public SessionHandlerLatchCounter(Session session, AtomicLong counter,
            CountDownLatch latch) {
        super(session);
        this.counter = counter;
        this.latch = latch;
    }

    @Override
    public void onNetworkMessage(NetworkEvent event) {
        counter.incrementAndGet();
        latch.countDown();
        System.out.println("invoked onNetworkMessage");
    }

    @Override
    protected void onDisconnect(Event event)
    {
        counter.incrementAndGet();
        latch.countDown();
        System.out.println("invoked onDisconnect");
        super.onDisconnect(event);
    }

    public AtomicLong getCounter() {
        return counter;
    }

    public CountDownLatch getLatch() {
        return latch;
    }

}