Cannot get player object in custom load event!?!?

Discussion in 'Spigot Plugin Development' started by jusjus112, Mar 18, 2019.

  1. Hi guys,

    So I'm struggling a bit with a situation where I do not know what the best thing is what I can do at this point.
    I have tried everything I came up with.

    The situation is as follows:
    I've created a custom player object called "Member". I'm loading the player async from the database on post-login. The point is, it's an async load so it has sometimes delay attached to it because it's off the main thread. I'm calling the "MemberLoadEvent" when the plugin received his data from SQL.

    The question now is, how am I able to load the real player object in that MemberLoadEvent to send him a message or do something else?
    Cause the player isn't added to the server and I can't use Bukkit.getPlayer.

    Do I, for example, need to inherit the joinEvent. I'm really stuck at this question.
    Hope someone can help me out ;)
     
    #1 jusjus112, Mar 18, 2019
    Last edited: Mar 18, 2019
  2. Weaves

    Resource Staff

    Assuming that you also store the players uuid or name at least you could do Bukkit.getOfflinePlayer(uuid).getPlayer() Otherwise I think we will need more info.
     
  3. Aren't offline Players stored server based? Like, do they need to have joined before in order to get an offline player?
    Also, why do you need more information? I literally just explained it.

    How can I get a player object in my custom event that is getting called on a "AsyncPlayerPreLoginEvent"
     
  4. Weaves

    Resource Staff

    Yes they are per server. But if you have information about the player in your database then they would have joined before, correct? You said above, post-login but async. You also said Bukkit.getPlayer() did not work. My thought was you could get the player from offline player when appropriate. I probably shouldn't have wrote it as one statement. I needed more information because I dont fully understand the use case or what is in the Member object.
     
  5. Yes, I kinda understand where you're heading too. The usecase if as follows:

    I'm storing the uuid, name and other stuff.

    1. User joins
    2. AsyncPlayerPreLoginEvent is called
    3. I'm checking if the user exists in the database, otherwise create a new member object with the uuid.
    4. The MemberLoadEvent is called once the player data is retrieved from the db.
    5. I'm sending a message to the player. (Player Object not yet loaded, cause it's not created yet)

    But like, what if the player never has joined the server before?
    Is the OfflinePlayer object created on the sync post login? Or on the joinEvent?
    Cause that's the issue right now.

    I was also thinking of adding a cooldown when I call the memberloadevent, but I think there is no way to know how much time it takes to retrieve data from the database.
     
  6. Weaves

    Resource Staff

    I am not sure how the offline player is created. You are not going to be able to send a message to the player before there is a player object. Maybe you could create a repeating task that checks for the player object every tick until it finds one and then associates it with your member object and cancels itself. I would also cancel it if it runs more than so many ticks. There is likely a better way though.
     
  7. I don't know if it's a good way to do it, but you can use CompletableFuture stored in a Map, start the job (loading data from db) in your AsyncPlayerPreLoginEvent and in your PlayerJoinEvent get the CompletableFuture instance of this player and call #whenComplete and in this method call your event.

    Example: (MemberData is a class storing your information loaded from database)
    Code (Java):
    public class SpigotHelp extends JavaPlugin implements Listener
    {
        private Map<UUID, CompletableFuture<MemberData>> loadingTasks = new HashMap<>();

        @Override
        public void onEnable()
        {
            Bukkit.getPluginManager().registerEvents(this, this);
        }

        @EventHandler
        public void onPlayerJoin(PlayerJoinEvent e)
        {
            Player player = e.getPlayer();
            // Get the "task" started in the AsyncPlayerPreLogin
            CompletableFuture<MemberData> loadedData = loadingTasks.get(player.getUniqueId());
            if (loadedData != null)
            {
                loadedData.whenComplete((memberData, throwable) -> Bukkit.getPluginManager().callEvent(new MemberLoadEvent(player, memberData)));
                loadingTasks.remove(player.getUniqueId());
            }
        }

        @EventHandler
        public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e)
        {
            // Add a new entry in the Map with the CompletableFuture of this player
            loadingTasks.put(e.getUniqueId(), CompletableFuture.supplyAsync(() -> Database.getMemberData(e.getUniqueId())));
        }

        @EventHandler
        public void onMemberLoad(MemberLoadEvent e)
        {
            e.getPlayer().sendMessage("loaded");
        }

    }
    You can also do everything in the PlayerJoinEvent (Make your sql query async and use whenComplete for calling your event)
     
  8. Create a AsyncMemberLoadEvent and make your MemberLoadEvent synchronised. The player will be loaded in the sync event and you can use the async event to do async things