AsyncPlayerPreLoginEvent, Join Listener Problem

Discussion in 'Spigot Plugin Development' started by jusjus112, Jul 15, 2016.

  1. Hello guys,

    We are stuck with an issue that is following us around 1 year.
    So, im gone straight to the point.

    We have a AsyncPlayerPreLoginEvent and a Join Event.
    In that AsyncPlayerPreLoginEvent, we are setting up everything for the player, and put everything in our database. Now, everything is working for setting it up, but the problem is, we have a JoinEvent, there we get the user info from our database. But, that will give us NullPointers. Because the plugin says the player we put in the databse in login is null. So, when we check in SQL, we see the player in it.

    So, is there a way it can be null?
    And the most important thing, is this fixable?
     
  2. Choco

    Moderator

    It's possible that since the first event is asynchronous, that it hasn't quite added the information completely yet as soon as the synchronous JoinEvent is occurring
     
    • Agree Agree x 1
  3. I wonder if you could use just PlayerPreLoginEvent to parse it.
    I also wonder if you could delay the player from joining the server until the file has fully parsed in your database.
     
  4. Shouldn't you be getting data out of your database instead of setting it in the database on AsyncPreLogin?

    That's most likely your problem, you are getting a bag of air out of your database.

    1. Use the AsyncPlayerPreLogin to load the data
    2. Use the PlayerJoinEvent to check if the data has been loaded properly (e.g add them to a hashmap on AsyncPlayerPreLogin after you loaded the data)
    3. Use the PlayerQuitEvent to store the data.
     
    • Agree Agree x 1
  5. Ya, im using it to load stuff from the databse. But, when the player doesnt exist, im creating a account for him.
    The loading part is working fine, but the creating part gives me errors.. And it wont load onjoin
     
  6. Do what I told you to do.
     
  7. Why not just handle everything in the same event?

    onPlayerJoinEvent
    • Add player to database if he/she doesn't exist
    • Get information from database

    Is there a reason you need to use two different events? Is there something you can get from one event listener that you can't get from the other?
     
    • Agree Agree x 1
  8. Use your async event to either retrieve the data, or create the player instance (and upload it at a later point).

    You then use your synchronous methods to retrieve it in a thread-safe manner. The thread safety is important, using a lock or thread-safe collection is ideal (I prefer a lock over the collection). You want to make as little queries to a database as possible. If you already have the info you're uploading, it makes no sense to then immediately download it again.
     
  9. Create and load the data from your async event and store it in some class instance. Store the class instance in a volatile variable so that all threads will be able to see the changes.
     
  10. Retrieving information from a database CAN take an extremely long time (talking about seconds here). Since PlayerJoinEvent is called on the main thread, this could stall the server for at least n seconds (possibly crashing the server).



    When using AsyncPlayerPreLoginEvent and PlayerJoinEvent (or PlayerLoginEvent):

    Remember that players can quit the connection early, and thus cause a memory leak (the plugin will never be notified of the player quitting the connection early, as no such event exists).
     
    #10 Spottedleaf, Jul 16, 2016
    Last edited: Jul 16, 2016
    • Like Like x 1
  11. R-R-R-Race conditions!
     
    • Funny Funny x 1
  12. Serializator

    Supporter

    Not in this case, the AsyncPlayerPreLoginEvent will wait until the player is added to the map and only call the PlayerJoinEvent when everything in the AsyncPlayerPreLoginEvent is done.
     
    • Useful Useful x 1
  13. My bad, assumptions on my part.
     
    • Funny Funny x 1
  14. No, he's correct.


    HashMap is not thread-safe. It is therefore subject to consequences (undefined behaviour) of two or more concurrent writes, if and when they happen.
    Even if no concurrent writes were made, HashMap does enforce a happens-before relationship. However, ConcurrentMap does:

    That means that an object placed into a HashMap by one thread and read by another thread may see the object but NOT the correct content of the object.

    Re-write for clarity:
    When a thread places an object in a HashMap and if another thread reads that object, the reading thread is not guaranteed to see the constructed contents of the object. The object may be fully un-constructed or half-constructed, the result is not defined (except that the value of a field can be its default or its constructed value, however final fields are guaranteed to be seen as their constructed value, as long as the constructor did not leak the 'this' reference to other threads while constructing).
     
    #14 Spottedleaf, Jul 16, 2016
    Last edited: Jul 16, 2016
  15. Serializator

    Supporter

    I was talking about the race condition in terms of the PlayerJoinEvent being called before everything was done in the AsyncPlayerPreLoginEvent, not about any other possible concurrency issues.
     
    • Informative Informative x 1
  16. I said e.g lol, thanks for the info, all though I don't need it since I know what I'm doing.