getWorld form offline player

Discussion in 'Plugin Development' started by T2PO, Nov 17, 2023.

Thread Status:
Not open for further replies.
  1. Offline

    T2PO

    Hey,

    i needed a plugin that would show me the current world for all offline players (who were previously on the server). This will probably only be possible with nbt (because Bukkit of type getOfflinePlayers cannot query a current world). Not individually but for all in world/playerdata (for over 100).

    I tried this, but unfortunately it didn't work.

    Code:
    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.World;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandSender;
    import org.bukkit.command.TabCompleter;
    import org.bukkit.entity.Player;
    import org.bukkit.plugin.java.JavaPlugin;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    public class Main extends JavaPlugin {
    
        @Override
        public void onEnable() {
            // Plugin-Aktivierung
        }
    
        @Override
        public void onDisable() {
            // Plugin-Deaktivierung
        }
    
        @Override
        public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
            if (command.getName().equalsIgnoreCase("offlineplayers")) {
                List<String> offlinePlayerList = new ArrayList<>();
    
                for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) {
                    if (isOffline(offlinePlayer)) {
                        offlinePlayerList.add(offlinePlayer.getName());
                    }
                }
    
                if (offlinePlayerList.isEmpty()) {
                    sender.sendMessage(ChatColor.RED + "Es sind keine Offline-Spieler verfügbar.");
                } else {
                    sender.sendMessage(ChatColor.GREEN + "Liste der Offline-Spieler:");
                    for (String playerName : offlinePlayerList) {
                        String worldName = getOfflinePlayerWorld(playerName);
                        sender.sendMessage(ChatColor.YELLOW + playerName + " ist/war in der Welt: " + worldName);
                    }
                }
    
                return true;
            }
    
            return false;
        }
    
        private boolean isOffline(OfflinePlayer player) {
            return !player.isOnline();
        }
    
        private String getOfflinePlayerWorld(String playerName) {
            OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
            UUID playerId = offlinePlayer.getUniqueId();
    
            for (World world : Bukkit.getWorlds()) {
                if (world.getPlayers().stream().anyMatch(player -> player.getUniqueId().equals(playerId))) {
                    return world.getName();
                }
            }
    
            return ChatColor.RED + "Unbekannte Welt";
        }
    }
     
  2. Online

    timtower Administrator Administrator Moderator

    @T2PO And why do you need to know in which world?
     
  3. Offline

    T2PO

    Unfortunately I have to change the plugin for separate inventories per world and I want to find out beforehand who is still on a certain world (to inform him if necessary, otherwise the inventory will be lost).
     
  4. Online

    timtower Administrator Administrator Moderator

    Have you considered opening up the files for that plugin? Probably has storage in the form of YML or a folder structure that is clear to read.
     
  5. Offline

    T2PO

    Mh that's an idea, but there are over 700 files which I would then have to search through.
     
  6. Online

    timtower Administrator Administrator Moderator

    Which plugin are you gonna remove?
    Please post a link, and not just a name.
     
  7. Offline

    T2PO

  8. Online

    timtower Administrator Administrator Moderator

  9. Offline

    T2PO

    I have been getting these errors since 1.20.2:

    Code:
    [15:48:50 INFO]: [ACF] Can't read players locale, you will be unable to automatically detect players language. Only Bukkit 1.7+ is supported for this.
    [15:48:50 INFO]: [ACF] java.lang.NoSuchFieldException: locale
    
    [15:48:50 INFO]: [ACF]    at java.base/java.lang.Class.getDeclaredField(Class.java:2642)
    
    [15:48:50 INFO]: [ACF]    at perworldinventory-kt-2.3.2.jar//me.ebonjaeger.perworldinventory.command.acf.BukkitCommandManager.readPlayerLocale(BukkitCommandManager.java:313)
    
    [15:48:50 INFO]: [ACF]    at perworldinventory-kt-2.3.2.jar//me.ebonjaeger.perworldinventory.command.acf.ACFBukkitListener.onPlayerJoin(ACFBukkitListener.java:54)
    
    [15:48:50 INFO]: [ACF]    at com.destroystokyo.paper.event.executor.MethodHandleEventExecutor.execute(MethodHandleEventExecutor.java:40)
    
    [15:48:50 INFO]: [ACF]    at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:81)
    
    [15:48:50 INFO]: [ACF]    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:70)
    
    [15:48:50 INFO]: [ACF]    at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:54)
    
    [15:48:50 INFO]: [ACF]    at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.callEvent(PaperPluginManagerImpl.java:126)
    
    [15:48:50 INFO]: [ACF]    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:615)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.players.PlayerList.a(PlayerList.java:325)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.network.ServerConfigurationPacketListenerImpl.a(ServerConfigurationPacketListenerImpl.java:130)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.network.protocol.configuration.ServerboundFinishConfigurationPacket.a(ServerboundFinishConfigurationPacket.java:18)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.network.protocol.configuration.ServerboundFinishConfigurationPacket.a(ServerboundFinishConfigurationPacket.java:9)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.network.protocol.PlayerConnectionUtils.lambda$ensureRunningOnSameThread$0(PlayerConnectionUtils.java:53)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.TickTask.run(TickTask.java:18)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.util.thread.IAsyncTaskHandler.d(IAsyncTaskHandler.java:153)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.util.thread.IAsyncTaskHandlerReentrant.d(IAsyncTaskHandlerReentrant.java:24)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:1324)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.d(MinecraftServer.java:193)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.util.thread.IAsyncTaskHandler.x(IAsyncTaskHandler.java:126)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.bg(MinecraftServer.java:1301)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.x(MinecraftServer.java:1294)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.util.thread.IAsyncTaskHandler.bp(IAsyncTaskHandler.java:114)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.a(MinecraftServer.java:1410)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:1156)
    
    [15:48:50 INFO]: [ACF]    at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:315)
    
    [15:48:50 INFO]: [ACF]    at java.base/java.lang.Thread.run(Thread.java:833)
    
    
    
    In addition, Paper Timings will be removed very soon:

    Code:
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Commands' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 INFO]: [ACF] Enabled Asynchronous Tab Completion Support!
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory help' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory version' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory reload' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory convert' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group create' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group addworld' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group info' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group list' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group delete' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group removeworld' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory group setrespawn' - this is deprecated behavior, please report it to the authors: EbonJaeger
    [15:45:36 WARN]: Plugin 'PerWorldInventory' is creating timing 'Command: perworldinventory migrate' - this is deprecated behavior, please report it to the authors: EbonJaeger
    
    But it still seems to work. Maybe it's just small things.
     
  10. Online

    timtower Administrator Administrator Moderator

  11. Offline

    T2PO

  12. Offline

    Tim_M

    You could just save player world locations in a big UUID hashmap, and save it to file when the server stops. (auto saving every once in a while would be a good idea as well in case of crashes)
     
  13. Offline

    T2PO

    Do you have a suggestion for the code?
     
  14. Offline

    Tim_M

    I don't have my pc right now, but it should be as simple as adding a playerquitevent, and saving/loading the hashmap from disk, which is pretty simple with .yml files. Just store world names with UUID as the key. To autosave make a repeating bukkit runnable.
     
  15. Offline

    T2PO

    I don't think you've fully understood the problem. I meant offline players who are not online and have not been online for a long time. Saving the data for the future is not what I meant.
     
  16. Offline

    Tim_M

    Their locations is stored in the world files. In worst case you can make a Python script to extract that data and convert it to the yml file. All future cases can be handled by the plugin.

    Edit: it's pretty strange that this huge API doesn't have a function for offline players to see where they've logged off. So either do the Python thing or integrate the world files reading right into your plugin.
     
    Last edited: Nov 19, 2023
  17. Offline

    T2PO

    It is also incomprehensible to me why there is nothing for this in the API. Unfortunately, I don't know how to convert the player data (nbt) into a yml.
     
  18. Online

    timtower Administrator Administrator Moderator

    Because the base game does not support having multiple inventories.
    There is no need to check which player has been in which world.

    Why do you want to know this anyways? To tell people to do something about their per world inventories?
    How are you gonna contact them?
     
  19. Offline

    T2PO

    To tell players to come online and change the world. There are several ways to contact players (mail, discord etc).

    There are certainly several reasons to request the location of offline players other than multiple inventories.
     
  20. Online

    timtower Administrator Administrator Moderator

    Location != has been in a certain world and has an inventory there.
    And most servers don't have ways to contact everybody or anyone at all.
     
  21. Offline

    T2PO

    I am sure that there are other reasons than multiple inventories.
     
  22. Offline

    EvilWitchdoctor

    For the general problem, I think this advice is best:
    And for this:
    I would recommend some small script (python?) to iterate over the files and extract the data you need.

    For the more specific question though, of "what world did an OfflinePlayer log out in", the only way I know to do it is by reading the .dat files, in <server dir>/<world>/playerdata/<uuid>.dat

    These files can be opened by external MC-editor tools, but technically can also be accessed in Bukkit API:
    Code:
    Player player;
    player.saveData(); // Saves to <world>/playerdata/<uuid>.dat
    player.loadData(); // Loads from <world>/playerdata/<uuid>.dat
    
    These are run automatically when a player joins/disconnects.

    So if you wanted, you could rename a .dat file to your own account's UUID, and load someone else's data, which includes the world they logged out in in... I don't really want to recommend this as a "solution" since it is very easy to accidentally overwrite or lose data for any accounts involved, but it's technically an option.

    I've used it before for viewing offline inventories, the code is a bit dense, but you can view it here if curious.
     
  23. Offline

    Dogged

    Just access the player's persistent data container every time they switch worlds and store the world name they switched to in the container as a NamespacedKey. Whenever they leave the server, you can just access this NamespacedKey again to check what world they were in last.

    Or, can you not just do player.getWorld() whenever they leave the server? The event triggers just before they leave, does it not?

    Code:
    //when they switch worlds
    PersistentDataContainer container = player.getPersistentDataContainer();
    NamespacedKey world = new NamespacedKey(plugin, "World");
    container.set(world, PersistentDataType.STRING, p.getWorld().getName());
    
    //when they leave the server
    PersistentDataContainer container = player.getPersistentDataContainer();
    NamespacedKey world = new NamespacedKey(plugin, "World");
    String world = container.get(world, PersistentDataType.STRING);
    
     
  24. Online

    timtower Administrator Administrator Moderator

    User wants to know this for players that haven't been on the server for a while. So adding data won't do the trick.
     
  25. Offline

    T2PO

    I have now done the same. Notepad++ can, for example, search for content in many yml files.

    However, I would like to have a simple query by command.
     
Thread Status:
Not open for further replies.

Share This Page