Logout users from databse, cannot get data because user and session already destroyed?

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

Logout users from databse, cannot get data because user and session already destroyed?

Post by SmartfoxEnjoyer »

Hello again.

I am trying to logout an account in my DB, but I get the following traces in my SFS2X server terminal:

Code: Select all

16:24:30,115 INFO  [SocketReader] sessions.DefaultSessionManager     - Session removed: { Id: 4, Type: DEFAULT, Logged: Yes, IP: 127.0.0.1:64585 }
16:24:30,115 INFO  [SFSWorker:Sys:3] api.SFSApi     - User disconnected: { Zone: ASOZone0 }, ( User Name: qwerty, Id: 3, Priv: 1, Sess: 127.0.0.1:64585 ) , SessionLen: 9904, Type: Unity
16:24:30,116 WARN  [SFSWorker:Ext:3] managers.SFSExtensionManager     - java.lang.NullPointerException:
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Exception: java.lang.NullPointerException
Message: *** Null ***
Description: Error during event handling: java.lang.NullPointerException, Listener: { Ext: ZoneExtension, Type: JAVA, Lev: ZONE, { Zone: ASOZone0 }, {} }
+--- --- ---+
Stack Trace:
+--- --- ---+
helper.DBManager.Logout(DBManager.java:30)


This happens like so:

Code: Select all

public class ZoneExtension extends SFSExtension {
   @Override
   public void init() {
      
      addEventHandler(SFSEventType.USER_DISCONNECT, UserDisconnectHandler.class);
      
      }


Then:

Code: Select all

public class UserDisconnectHandler extends BaseServerEventHandler {

    @Override
    public void handleServerEvent(ISFSEvent event) throws SFSException {
        DBManager.Logout((Session) event.getParameter(SFSEventParam.SESSION));
    }
}


Finally:

Code: Select all

public class DBManager {
    static String _sqlUpdate = "UPDATE main.accounts SET online=? WHERE id=?";

    public static void Logout(Session session) {
        try {
            var _sqlParams = new Object[2];
            _sqlParams[0] = "F";
            _sqlParams[1] = session.getProperty("account_id"); // nullReference happens here, because as terminal says user and session has already been disconnected/removed/destroyed...

            _dbManager.executeUpdate(_sqlUpdate, _sqlParams);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


Is there an event that fires before the user is disconnected and session is removed? I need to access or atleast store the session properties somewhere just before they get destroyed.
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

Re: Logout users from databse, cannot get data because user and session already destroyed?

Post by SmartfoxEnjoyer »

So I ended up storing the "username" inside the Session in the LoginPostProcess, then using the JoinZoneEvent I got the User, and queried the database using the username, through User.getSession().getProperty("username"), collect account_id from DB and set account as online in DB aswell, next use User.setProperty to store the account_id value. Finally on the UserDisconnectEvent, it does supply the User variable and it is still accesible even after DC, so I can then do user.getProperty("account_id") to update the DB and set online for that account to false again.

Its a big workaround, but I guess thats just the way it is since session is unavailable after DC so storing variables in the session is a no go. But User variables stay accessible after a DC so thats the way to do it, it seems.

Hope this helps someone else in the future!
User avatar
Lapo
Site Admin
Posts: 23438
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Logout users from databse, cannot get data because user and session already destroyed?

Post by Lapo »

Hi,
from your snippets it looks like you're handling the USER_DISCONNECT event.
The event passes a bunch of parameters, one is the User itself, from which you can obtain the name:

Code: Select all

String name = user.getName()


I am not sure what's the need for the workaround you have described.

Cheers
Lapo
--
gotoAndPlay()
...addicted to flash games
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

Re: Logout users from databse, cannot get data because user and session already destroyed?

Post by SmartfoxEnjoyer »

Hi Lapo, you are correct, getting the user in disconnect is better, but still I have the same issue where I need to store the username or account_id inside the user upon login, I currently try to store it in the LoginPostProcess in the loginData.session, but for some reason my LoginPostprocess execute function isnt getting called anymore either...

heres my code:

C# Client:

Code: Select all

public static class SFS_Login
{
    static string CMD_SUBMIT = "$Login.Submit";

    public static void RegisterListeners()
    {
        // Add listeners to SFS Client Instance
        Debug.Log("registering SFS_Login Listeners");
        SFS.Client.RemoveEventListener(SFSEvent.EXTENSION_RESPONSE, onLoginResponse);

        SFS.Client.AddEventListener(SFSEvent.EXTENSION_RESPONSE, onLoginResponse);
    }

    public static void SendLoginData(string username, string pass)
    {
    // this gets called when I login
        Debug.Log("Login user: " + username + ", plain pass: " + pass);
        string md5Pass = PasswordUtil.MD5Password(pass);

        SFS.Client.Send(new Sfs2X.Requests.LoginRequest(username,
                                                        md5Pass,
                                                        SFS.Client.Config.Zone));
    }

    static void onLoginResponse(BaseEvent evt)
    {
    // This also never gets called for some reason
        string cmd = (string)evt.Params["cmd"];
        Debug.Log("Received loginResponse: " + cmd);

        SFSObject sfso = (SFSObject)evt.Params["params"];

        if (cmd == CMD_SUBMIT)
        {
            if (sfso.GetBool("success"))
            {
                Debug.Log("Success, thanks for logging in.");
            }
            else
                Debug.Log("Login error:" + (string)evt.Params["errorMessage"]);
        }
    }
}



The weird thing is, it was working before... But now responses are not getting triggered on client side and LoginAssistant Post/Preprocess are not getting triggered on server side.

Code: Select all

public class ZoneExtension extends SFSExtension {
   ISFSApi API;

   DBManager _dbManager;
   Login _login;

   @Override
   public void init() {
      API = getApi();

      trace("ZoneExtension -- started");

      _dbManager = new DBManager();
      _dbManager.Init(getParentZone().getDBManager(), this);
      trace("DBManager -- started");

      _login = new Login();
      _login.Init(this); // Login Assistant
      trace("Login Assistant -- started");

      addEventHandler(SFSEventType.USER_JOIN_ZONE, UserJoinZoneEventHandler.class);
      addEventHandler(SFSEventType.USER_DISCONNECT, UserDisconnectEventHandler.class);

      addRequestHandler("Characters", RequestCharactersHandler.class);
      addRequestHandler("CreateCharacter", RequestCreateCharacterHandler.class);
      addRequestHandler("SelectCharacter", RequestSelectCharacterHandler.class);
      addRequestHandler("DeleteCharacter", RequestDeleteCharacterHandler.class);
      
      addRequestHandler("MigrateServer", RequestMigrateServerHandler.class);
   }

   @Override
   public void destroy() {
      super.destroy();
      _login.LAC.destroy();
      trace("ZoneExtension -- stopped");
   }

}


My LoginAssistant:

Code: Select all

package login;

import java.util.Arrays;

import com.smartfoxserver.v2.components.login.LoginAssistantComponent;
import com.smartfoxserver.v2.extensions.ISFSExtension;

import zone.ZoneExtension;

public class Login {

   public LoginAssistantComponent LAC;
   String sqlSelect = "SELECT * FROM main.accounts WHERE username=?";

   Object[] sqlParams;

   ZoneExtension ext;

   public void Init(ISFSExtension ext) {
      this.ext = (ZoneExtension) ext;

      LAC = new LoginAssistantComponent(ext);

      // Configure the component
      LAC.getConfig().loginTable = "main.accounts";
      LAC.getConfig().userNameField = "username";
      LAC.getConfig().passwordField = "hash";

      LAC.getConfig().useCaseSensitiveNameChecks = true;

      LAC.getConfig().extraFields = Arrays.asList("id", "activated", "banned");

      // LAC.getConfig().customPasswordCheck = true;
      LAC.getConfig().preProcessPlugin = new LoginPreProcessHandler();

      LAC.getConfig().postProcessPlugin = new LoginPostProcessHandler(); // This function doesnt get called for some reason?
   }
}


PostProcess:

Code: Select all

public class LoginPostProcessHandler implements ILoginAssistantPlugin {

    @Override
    public void execute(LoginData ld) throws SFSLoginException {

        DBManager.Login(ld.userName); // See next code block

        ld.session.setProperty("$permission", DefaultPermissionProfile.STANDARD);

        ld.session.setProperty("username", ld.userName); // Set username inside session
    }
}


How I know PostProcess isnt being called:

Code: Select all

    public static void Login(String username) {
        try {
            _sqlParams = new Object[2];
            _sqlParams[0] = "T";
            _sqlParams[1] = username;

            _dbManager.executeUpdate(_sqlUpdateAccountOnline, _sqlParams);

            ext.trace("DB Logged in user: " + username); // This trace never appears in Terminal?
            // Thats why i think LoginPostProcess isnt being called at all for some reason?

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


When I try to retrieve the username in another script a few frames after login I get "username : nullReferenceException":

Code: Select all

public static void GetCharacters(User user) {
        try {
            _sqlParams = new Object[1];
            _sqlParams[0] = user.getSession().getProperty("username"); // username = null? why? I stored the username in session in postProcess

            ext.trace("username: " + _sqlParams[0]); // this traces as username: null

            // Get accountID by username and store in User
            ISFSArray accountIDArray = _dbManager.executeQuery(_sqlSelectAccountID, _sqlParams);
            int accountID = accountIDArray.getSFSObject(0).getInt("id"); // this gives an index out of bounds because the username was null

            user.setProperty("account_id", accountID);

            // Get all characters by accountID
            _sqlParams[0] = accountID;
            ISFSArray dbCharacters = _dbManager.executeQuery(_sqlSelectCharacters, _sqlParams);

            if (dbCharacters.size() == 0) {
                ext.SendNoCharacters(user);
                return;
            }

            // Parse the DBCharacters to get correct types
            ISFSArray parsedCharacters = ParseCharacters(dbCharacters);

            parsedCharacters = ISFSExtensions.SortSFSOsByComparator(parsedCharacters, "id");

            SFSObject parsedCharactersSFSO = new SFSObject();
            parsedCharactersSFSO.putSFSArray("Characters", parsedCharacters);

            ext.SendCharacters(user, parsedCharactersSFSO);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


Any idea what is going on here?

TL:DR:

1.PostProcessLogin => store username in session (Doesnt seem to happen at all because my traces dont get called and dont appear in terminal)
2. retrieve username from session a few frames later => its gone/null

Terminal Traces:

Code: Select all

21:50:57,750 INFO  [SocketReader] sessions.DefaultSessionManager     - Session removed: { Id: 42, Type: DEFAULT, Logged: Yes, IP: 127.0.0.1:51589 }
21:50:57,750 INFO  [SFSWorker:Sys:1] api.SFSApi     - User disconnected: { Zone: GuestZone }, ( User Name: Guest#37, Id: 37, Priv: 0, Sess: 127.0.0.1:51589 ) , SessionLen: 27, Type: Unity
------------------------------------------------------------------
----- I added these 3 lines to make it more readable --------
------------------------------------------------------------------
21:51:05,772 INFO  [SocketReader] sessions.DefaultSessionManager     - Session created: { Id: 43, Type: DEFAULT, Logged: No, IP: 127.0.0.1:51592 } on Server port: 9933 <---> 51592
21:51:05,794 INFO  [SFSWorker:Ext:1] api.SFSApi     - User login: { Zone: ASOZone0 }, ( User Name: qwerty, Id: 38, Priv: 1, Sess: 127.0.0.1:51592 ) , Type: Unity
21:51:05,796 INFO  [SFSWorker:Ext:1] api.SFSApi     - Room joined: [ Room: CharacterSelection, Id: 0, Group: default, isGame: false ], { Zone: ASOZone0 }, ( User Name: qwerty, Id: 38, Priv: 1, Sess: 127.0.0.1:51592 ) , asSpect: false
21:51:05,822 INFO  [SFSWorker:Ext:2] Extensions     - {ZoneExtension}: Received client request: RequestCharacters
21:51:05,823 INFO  [SFSWorker:Ext:2] Extensions     - {ZoneExtension}: username: null


As you can see :
1. Session Created
2. User Login
3. LoginPosProcess isnt being called
4. Room Joined
5. Username is null when trying to retrieve it from session

For what reason could the server skip PostProcessPlugin? Or is something else going on?
Im using latest sfs2x server 2.19.3
And latest C# API 1.8.3
Thanks.

SFS2X Server Extension settings:
NoCustomLogin.png
(67.1 KiB) Not downloaded yet

OnlyThing I changed.png
(57.74 KiB) Not downloaded yet

ZoneExtSettings.png
(47.51 KiB) Not downloaded yet
User avatar
Lapo
Site Admin
Posts: 23438
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Logout users from databse, cannot get data because user and session already destroyed?

Post by Lapo »

I would start by checking the logs and looking for exceptions. If the postProcess class is not invoked I'd expect some error to prevent it from running.

Cheers
Lapo
--
gotoAndPlay()
...addicted to flash games
SmartfoxEnjoyer
Posts: 93
Joined: 13 Dec 2023, 20:39

Re: Logout users from databse, cannot get data because user and session already destroyed?

Post by SmartfoxEnjoyer »

Lapo wrote:I would start by checking the logs and looking for exceptions. If the postProcess class is not invoked I'd expect some error to prevent it from running.

Cheers


Hi Lapo, do all exceptions trace into the terminal or are there exceptions that are only visible inside the AdminTool Analytics Tab?

Because there are no exceptions in my Terminal when I login.

Here is my runtime logs:

Code: Select all


// -------- CONNECT AS GUEST TO REQUEST SERVER LIST
17:04:06,225 INFO  [SocketReader] sessions.DefaultSessionManager     - Session created: { Id: 2, Type: DEFAULT, Logged: No, IP: 127.0.0.1:50095 } on Server port: 9933 <---> 50095
17:04:06,404 INFO  [SFSWorker:Sys:1] api.SFSApi     - User login: { Zone: GuestZone }, ( User Name: Guest#1, Id: 1, Priv: 0, Sess: 127.0.0.1:50095 ) , Type: Unity
17:04:06,430 INFO  [SFSWorker:Ext:2] Extensions     - {GuestExtension}: Received client request: Servers
17:04:06,438 INFO  [SFSWorker:Ext:2] Extensions     - {GuestExtension}: Sent back to client: Servers

//////// ----- DISCONNECT GUEST CONNECTION AFTER RECEIVE SERVERS
17:04:06,457 INFO  [SocketReader] sessions.DefaultSessionManager     - Session removed: { Id: 2, Type: DEFAULT, Logged: Yes, IP: 127.0.0.1:50095 }
17:04:06,458 INFO  [SFSWorker:Sys:3] api.SFSApi     - User disconnected: { Zone: GuestZone }, ( User Name: Guest#1, Id: 1, Priv: 0, Sess: 127.0.0.1:50095 ) , SessionLen: 54, Type: Unity

// -------- CONNECT AS GUEST TO JOIN SERVER
17:04:08,058 INFO  [SocketReader] sessions.DefaultSessionManager     - Session created: { Id: 3, Type: DEFAULT, Logged: No, IP: 127.0.0.1:50096 } on Server port: 9933 <---> 50096
17:04:08,075 INFO  [SFSWorker:Sys:2] api.SFSApi     - User login: { Zone: GuestZone }, ( User Name: Guest#2, Id: 2, Priv: 0, Sess: 127.0.0.1:50096 ) , Type: Unity
17:04:08,091 INFO  [SFSWorker:Ext:1] Extensions     - {GuestExtension}: Received client request: JoinServer
17:04:08,096 INFO  [SFSWorker:Ext:1] Extensions     - {GuestExtension}: Looking for zone: ASOZone0 , found zone: BasicExamples
17:04:08,096 INFO  [SFSWorker:Ext:1] Extensions     - {GuestExtension}: Looking for zone: ASOZone0 , found zone: ASOZone0
17:04:08,100 INFO  [SFSWorker:Ext:1] Extensions     - {GuestExtension}: Zone Max capacity: 5000 , users online: 0
17:04:08,101 INFO  [SFSWorker:Ext:1] Extensions     - {GuestExtension}: Sent back to client: ServerChangeAccepted

//////// ----- DISCONNECT GUEST CONNECTION AFTER RECEIVE JOIN SERVER ACCEPTED
17:04:08,124 INFO  [SocketReader] sessions.DefaultSessionManager     - Session removed: { Id: 3, Type: DEFAULT, Logged: Yes, IP: 127.0.0.1:50096 }
17:04:08,124 INFO  [SFSWorker:Sys:4] api.SFSApi     - User disconnected: { Zone: GuestZone }, ( User Name: Guest#2, Id: 2, Priv: 0, Sess: 127.0.0.1:50096 ) , SessionLen: 49, Type: Unity

//////// ----- CONNECT AS USER TO LOGIN
17:04:14,678 INFO  [SocketReader] sessions.DefaultSessionManager     - Session created: { Id: 4, Type: DEFAULT, Logged: No, IP: 127.0.0.1:50098 } on Server port: 9933 <---> 50098
17:04:14,724 INFO  [SFSWorker:Ext:3] api.SFSApi     - User login: { Zone: ASOZone0 }, ( User Name: qwerty, Id: 3, Priv: 1, Sess: 127.0.0.1:50098 ) , Type: Unity
17:04:14,725 INFO  [SFSWorker:Ext:3] api.SFSApi     - Room joined: [ Room: CharacterSelection, Id: 0, Group: default, isGame: false ], { Zone: ASOZone0 }, ( User Name: qwerty, Id: 3, Priv: 1, Sess: 127.0.0.1:50098 ) , asSpect: false
//// ------- no extensionExceptions or custom traces confirming that preProcess and PostProcess have been executed....


Is it maybe a problem that I ( connect as guest > fire extension request > receive response > disconnect guest ) multiple times before finally connecting as a real user?

I have 2 Zones, GuestZone, running GuestExtension, which handles server selection and sign up as these are guest requests
The other zone is ASOZone0, running ZoneExtension which i use for Login and Character Selection as I showed in my previous post

I know for sure that when I login the user it logs into the Zone ASOZone0 (ZoneExtension) and that when i login as Guest it logs into the GuestZone/GuestExtension.

Finally I have GuestExtension running the SignUp Assistant and ZoneExtension running the Login Assistant. Is that maybe the problem?

I think I will just stop using the Login Assistant all together and use a custom login at this point as its now the second day that i cant get PreProcess and PostProcess to execute anymore and its hindering my progress.

I would still like to know if you have any other ideas of what may be causing this, if there are no exceptions like i showed.

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

Re: Logout users from databse, cannot get data because user and session already destroyed?

Post by SmartfoxEnjoyer »

OK WTF

I did a lot of trial and error, trying to remove the Login class and adding the loginAssistant directly inside the extension, still it didnt work.
Next i removed my PreProcess class and used an anonymous method inside the extension again to setup the PreProcess, and somehow that worked!

I made a new package > made 3 new classes, Login, LoginPreProcess and loginPostProcess and copied the old code into these new classses, then i deleted the old (corrupted?) classes.
Next i setup my zoneExtension as before, ZoneExt > Login.class > LoginAssistant.preProcesPlugin = new LoginPreProcess();

And it all works just like it used to work before (3 days ago).
So somehow, someway my classes got corrupted and while I was setting preProcessPlugin = MyCorruptedClass, it actually resulted in preProcessPlugin = null;

This didnt throw any exceptions and was very hard to find out because it just happens somehow, I dont fault SFS at all though, it was a JAVA class problem or maybe my IDE fucked up somehow!

Anyway im really glad I solved this because after 2 days i was starting to pull my hair out lmao (Why TF isnt my pre/post process being called!!!??)

Maybe this will help someone else in the future...

If it isnt running, maybe your class got corrupted and will be null when you set it somewhere!

Thanks anyway Lapo!

Cheers!
Post Reply