After disconnect, user is still logged in to zone/sfsAPI

Post here your questions about SFS2X. Here we discuss all server-side matters. For client API questions see the dedicated forums.

Moderators: Lapo, Bax

Post Reply
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

After disconnect, user is still logged in to zone/sfsAPI

Post by SmartfoxEnjoyer »

Im facing an issue where after a user disconnects, the server traces the message from internal sfsAPI that the session and user have been disconnected as expected.
However when the same user then tries to login, he gets a user already logged in error:

Code: Select all

// FIRST LOGIN:
22:09:56,138 INFO  [SocketReader] sessions.DefaultSessionManager     - Session created: { Id: 1, Type: DEFAULT, Logged: No, IP: 127.0.0.1:65489 } on Server port: 9933 <---> 65489
22:09:56,189 INFO  [SFSWorker:Ext:3] Extensions     - {__lib__}: tester4@example.com
22:09:56,196 INFO  [SFSWorker:Ext:3] Extensions     - {__lib__}: Found join token in db, for user: tester4@example.com
22:09:56,196 INFO  [SFSWorker:Ext:3] Extensions     - {__lib__}: SUCCESS: Received join token matches and is not expired.
22:09:56,200 INFO  [SFSWorker:Ext:3] Extensions     - {__lib__}: Stored account_id 3 in session for email tester4@example.com
22:09:56,237 INFO  [SFSWorker:Ext:3] api.SFSApi     - User login: { Zone: MainZone }, ( User Name: tester4@example.com, Id: 0, Priv: 0, Sess: 127.0.0.1:65489 ) , Type: Unity
22:09:56,238 INFO  [SFSWorker:Ext:3] Extensions     - {ZoneExtension}: User joined zone.

22:09:56,282 INFO  [SFSWorker:Ext:1] Extensions     - {ZoneExtension}: Sent back to client: Characters
22:09:57,387 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Character 3 selected and marked online.
22:09:57,388 INFO  [SFSWorker:Ext:2] Extensions     - {ZoneExtension}: Character selected
22:09:57,390 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: BaseDamage: 40
22:09:57,390 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: AttackDamageBonus: 1.0
22:09:57,391 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Speed: 5.0
22:09:57,391 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: HealthMax: 200 -> 0 + 0
22:09:57,394 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: HealthMax+= 5
22:09:57,394 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: AttackDamageBonus: 1.0
22:09:57,395 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Speed: 5.0
22:09:57,396 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: HealthMax: 352 -> 0 + 5
22:09:57,396 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: AttackDamageBonus: 1.04
22:09:57,397 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Speed: 5.78
22:09:57,398 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: HealthMax: 657 -> 10 + 5
22:09:57,399 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Added character for user : 0
22:09:58,587 INFO  [SFSWorker:Ext:1] api.SFSApi     - Room joined: [ MMORoom: Tutorial, Id: 1, Group: default, AOI: (50.0, 1.0, 50.0) ], { Zone: MainZone }, ( User Name: wolof, Id: 0, Priv: 0, Sess: 127.0.0.1:65489 ) , asSpect: false
22:09:58,587 INFO  [SFSWorker:Ext:1] Extensions     - {ZoneExtension}: joined into room in internal user room list?: [ MMORoom: Tutorial, Id: 1, Group: default, AOI: (50.0, 1.0, 50.0) ]
22:09:58,589 INFO  [SFSWorker:Ext:4] Extensions     - {RoomExtension}: Activated Node with index 1 and perimeter: X[0, 140], Y[-140, 0]
22:09:58,592 INFO  [SFSWorker:Ext:4] Extensions     - {RoomExtension}: User 0 entered room Tutorial

// FIRST LOGOUT ---> Session and User removed internally by sfsAPI and logged into our terminal(this is done by internal sfs api code):
22:10:00,012 INFO  [SocketReader] sessions.DefaultSessionManager     - Session removed: { Id: 1, Type: DEFAULT, Logged: Yes, IP: 127.0.0.1:65489 }
22:10:00,014 INFO  [SFSWorker:Sys:4] api.SFSApi     - User disconnected: { Zone: MainZone }, ( User Name: wolof, Id: 0, Priv: 0, Sess: 127.0.0.1:65489 ) , SessionLen: 3778, Type: Unity

// My own logout traces (not sfsAPI)
22:10:00,015 INFO  [SFSWorker:Ext:1] Extensions     - {ZoneExtension}: User 0 disconnect server event.
22:10:00,015 INFO  [SFSWorker:Ext:1] Extensions     - {__lib__}: Removing character from user : 0
22:10:00,047 INFO  [SFSWorker:Ext:1] Extensions     - {__lib__}: Character wolof (PID: 0 | dbId: 3) saved successfully.
22:10:00,054 INFO  [SFSWorker:Ext:1] Extensions     - {__lib__}: Logout: Marked character_id 3 offline. Rows updated: 1
22:10:00,058 INFO  [SFSWorker:Ext:1] Extensions     - {__lib__}: Logout: Logged event for character_id 3 (outcome: manual - time online: 00:00:02)
22:10:39,028 INFO  [pool-1-thread-4] Extensions     - {__lib__}: Periodic character save completed.

// SECOND LOGIN ATTEMPT ---> failed:  api.SFSApi     - Login error: Another user is already logged with the same name: tester4@example.com.
22:10:42,176 INFO  [SocketReader] sessions.DefaultSessionManager     - Session created: { Id: 2, Type: DEFAULT, Logged: No, IP: 127.0.0.1:65497 } on Server port: 9933 <---> 65497
22:10:42,208 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: tester4@example.com
22:10:42,208 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: ERROR: User is still logged in!
22:10:42,212 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Found join token in db, for user: tester4@example.com
22:10:42,212 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: SUCCESS: Received join token matches and is not expired.
22:10:42,213 INFO  [SFSWorker:Ext:2] Extensions     - {__lib__}: Stored account_id 3 in session for email tester4@example.com
22:10:42,219 INFO  [SFSWorker:Ext:2] api.SFSApi     - Login error: Another user is already logged with the same name: tester4@example.com. Requested by: { Id: 2, Type: DEFAULT, Logged: No, IP: 127.0.0.1:65497 }
22:10:42,229 INFO  [SocketReader] sessions.DefaultSessionManager     - Session removed: { Id: 2, Type: DEFAULT, Logged: No, IP: 127.0.0.1:65497 }
Login code, I dont think this is the issue, it just works as intended:

Code: Select all

package event;

import com.smartfoxserver.bitswarm.sessions.ISession;
import com.smartfoxserver.v2.core.ISFSEvent;
import com.smartfoxserver.v2.core.SFSEventParam;
import com.smartfoxserver.v2.entities.data.SFSObject;
import com.smartfoxserver.v2.exceptions.SFSException;
import com.smartfoxserver.v2.exceptions.SFSLoginException;
import com.smartfoxserver.v2.extensions.BaseServerEventHandler;

import database.DatabaseManager;
import manager.StaticResources;

public class LoginEventHandler extends BaseServerEventHandler
{
    @Override
    public void handleServerEvent(ISFSEvent event) throws SFSException
    {
        // Grab parameters from client request
        String email = (String) event.getParameter(SFSEventParam.LOGIN_NAME);

        var loggedInUser = getApi().getUserByName(email);
        StaticResources.Trace(email);

        if (loggedInUser != null)
        {
            // getParentExtension().getParentZone().removeUser(email); // doesnt work either
            StaticResources.Trace("ERROR: User is still logged in!");

            // getApi().logout(loggedInUser); // this doesnt work either, subsequent login attempts still fail...
        }

        var extraData = (SFSObject) event.getParameter(SFSEventParam.LOGIN_IN_DATA);
        short serverId = extraData.getShort("serverId");
        String token = (String) extraData.getUtfString("join_token");

        ISession session = (ISession) event.getParameter(SFSEventParam.SESSION);

        try
        {
            DatabaseManager.Login(session, email, token, serverId); // just updates db values
        }
        catch (SFSLoginException e)
        {
            // Rethrow SFSLoginException to propagate to client
            throw e;
        }
        catch (Exception e)
        {
            // Catch any unexpected exception and wrap in SFSLoginException
            throw new SFSLoginException("An unexpected error occurred during login: " + e.getMessage());
        }
    }
}

Disconnect code, this gets called after client API calls _sfs.Disconnect() as recommended in the docs and unity examples, OnApplicationQuit -> call sfs.Disconnect(), which then makes this event fire on server side as expected but somehow it isnt actually logging out our user:

Code: Select all

package event;

import com.smartfoxserver.v2.extensions.BaseServerEventHandler;

import database.DatabaseManager;
import manager.CharacterManager;
import manager.StaticResources;
import manager.ZoneManager;

import com.smartfoxserver.v2.core.ISFSEvent;
import com.smartfoxserver.v2.core.SFSEventParam;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.exceptions.SFSException;

public class DisconnectEventHandler extends BaseServerEventHandler
{
    @Override
    public void handleServerEvent(ISFSEvent event) throws SFSException
    {
        User user = (User) event.getParameter(SFSEventParam.USER);
        trace("User " + user.getId() + " disconnect server event.");

        ZoneManager.RemoveFromRoom(user); // remove from custom mapping, doesnt affect sfsAPI at all

        CharacterManager.RemoveCharacter(user);

        DatabaseManager.Logout(user, "manual");

        // getParentExtension().getParentZone().removeUser(user); // also doesnt work
        // StaticResources.Trace(user.getDump());
    }
}
Cheers
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

Re: After disconnect, user is still logged in to zone/sfsAPI

Post by SmartfoxEnjoyer »

As always, i happen to find the likely answer after searching for myself for hours but exactly after i post the question here on the forums...

Its probably because after login i change the user.userName because otherwise we leak the emails to other clients because other clients get OnProximityList updates and can read other user.name strings which is their email by default, so i change it to their logged in chosen character name when they login, thats probbaly why internal sfsAPI is failing to remove the user as the name has been changed?

Code: Select all

 public void OnCharacterSelected(User user, ISFSObject params)
    {
        long characterId = params.getLong("c");
        var result = DatabaseManager.SelectCharacter(user, characterId);
        if (result instanceof SFSObject)
        {
            var character = (SFSObject) result;
            trace("Character selected");

            user.setProperty("character_id", character.getLong("id"));
            user.setName(character.getUtfString("name")); // change so private emails doesnt leak to other clients

            CharacterManager.AddCharacter(user, character);

            SendSceneData(user, "Tutorial");
        }
        ...
        
So how can this be solved? Or is this not even the issue because interally sfs uses the user.getId() int and not the user.getname() string to remove users after a disconnect? Or is this the issue and internally sfsAPI uses the name?

Thanks.
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

Re: After disconnect, user is still logged in to zone/sfsAPI

Post by SmartfoxEnjoyer »

I can confirm, when I comment out this line after character selection:

Code: Select all

            // user.setName(character.getUtfString("name"));
Then everything works as expected again, user can login, logout, login, logout, as many times as he wants, no errors at all.

Cheers
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

Re: After disconnect, user is still logged in to zone/sfsAPI

Post by SmartfoxEnjoyer »

Found the answer, thanks.

https://smartfoxserver.com/blog/how-to- ... tom-login/
There is a simple convention that allows us to provide an alternative name to the login system. In the USER_LOGIN event we are passed an empty SFSObject that can be used to return custom data to the client. We just need to provide the name in that object under a very specific (and reserved) key name. See the code below:

public class LoginEventHandler extends BaseServerEventHandler
{
@Override
public void handleServerEvent(ISFSEvent event) throws SFSException
{
String name = (String) event.getParameter(SFSEventParam.LOGIN_NAME);
ISFSObject outData = (ISFSObject) event.getParameter(SFSEventParam.LOGIN_OUT_DATA);

// ...
// your login logic goes here
// ...

// Provide a new name for the user:
String newName = "User-" + name;
outData.putUtfString(SFSConstants.NEW_LOGIN_NAME, newName);
}
}
Post Reply