ProxioDev / ValioBungee

Synchronize players data between BungeeCord / Velocity proxies
Eclipse Public License 1.0
204 stars 52 forks source link

Transfer - UUID #72

Closed Espiiii-debug closed 5 months ago

Espiiii-debug commented 1 year ago

What is your question?

Good evening, I am interested in Redis and in particular its way of assigning uuids and storing them. How can I retrieve this data and modify the uuids assigned to the players or better, how can I transfer the uuids of the players from one bungee to another?

Last question, does redis-bungee sync player uuids across proxies?

Espiiii-debug commented 1 year ago

If you have a discord, I would be happy to discuss it with you in a simpler way than by issues !

ham1255 commented 1 year ago

discord: Ham1255#4547

Redis is just the database standalone software which RedisBungee uses to store data since Redis is really fast for such things, you can read the readme of the project to know what redis Lib RedisBungee uses.

RedisBungee does automatically add the uuid of player and remove them when they quit the network

and you can access these data on all proxies about a player:

also other data not related about players:

Espiiii-debug commented 1 year ago

Excuse my mistake, I wanted to write Bungeecord and not Redis, I should have read it again... Because I searched a bit but I can't find the place where the uuids generated for players by bungeecord are stored by default.

This most likely already exists but I was wondering what was the data transferred by jedis! I looked a bit in your wiki but I only saw a few examples!

ham1255 commented 1 year ago

sorry about the wiki its just really old from original redisbunge repository, so everything clear now?

Espiiii-debug commented 1 year ago

Not really, my question was rather, how do you synchronize the UUIDs and how bungeecord generates them, if you have the time to explain it to me, that's great, otherwise you won't take the lead.

Because looking at the dependencies used, you only use the API which is really surprising! I was expecting Bungee Native or other style dependencies to do this kind of sync!

ham1255 commented 1 year ago

i am not very good at explaining but if i get your question correctly, there is no need for using internal bungee stuff, basically uuid stuff is handled during login redis bungee does not change anything related to uuids, also when we sync the uuid which seen in https://github.com/ProxioDev/RedisBungee/blob/develop/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeBungeeListener.java see on join

note: i didn't create whole thing, i just made it modular in-case other proxy appears which in this case velocity

proxy init

proxy startup it starts 2 repeating tasks, heartbeat and integrity check

https://github.com/ProxioDev/RedisBungee/blob/develop/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/HeartbeatTask.java

heart beat keep adding/updating itself with timestamp in unix using redis server time stamps since you cannot trust that proxy is running with correct time settings, inside hashset

long redisTime = plugin.getRedisTime(unifiedJedis);
unifiedJedis.hset("heartbeats", plugin.getConfiguration().getProxyId(), String.valueOf(redisTime));

on join

when everything in the login event is passed like bans getting checked on login event then post login event get called and we call BungeePlayerUtils.createBungeePlayer(event.getPlayer(), unifiedJedis, true); and handle platform stuff like getting servername, which then method it self calls PlayerUtils.createPlayer(player.getUniqueId(), unifiedJedis, serverName, pendingConnection.getAddress().getAddress(), fireEvent); here is code for that:

public static void createPlayer(UUID uuid, UnifiedJedis unifiedJedis, String currentServer, InetAddress hostname, boolean fireEvent) {
        final boolean isKickedFromOtherLocation = isKickedOtherLocation(uuid.toString(), unifiedJedis);
        Map<String, String> playerData = new HashMap<>(4);
        playerData.put("online", "0");
        playerData.put("ip", hostname.getHostName());
        playerData.put("proxy", AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId());
        if (currentServer != null) {
            playerData.put("server", currentServer);
        }
        unifiedJedis.sadd("proxy:" + AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId() + ":usersOnline", uuid.toString());
        unifiedJedis.hset("player:" + uuid, playerData);
        if (fireEvent && !isKickedFromOtherLocation) {
            playerJoinPayload(uuid, unifiedJedis, hostname);
        }
    }

you can see we are using redis hashsets to store stuff like hostnameip of the player etc.

but you can also see sadd which basically adds the player uuid into the set of online players, since each proxy got it own ID.

getting synced uuids

so to get the players uuids we need to get current heart beating proxies which can be seen below

here is the method of getting all players uuids from data is stored in redis

default Set<UUID> getPlayers() {
        return new RedisTask<Set<UUID>>(this) {
            @Override
            public Set<UUID> unifiedJedisTask(UnifiedJedis unifiedJedis) {
                ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
                try {
                    List<String> keys = new ArrayList<>();
                    for (String i : getProxiesIds()) {
                        keys.add("proxy:" + i + ":usersOnline");
                    }
                    if (!keys.isEmpty()) {
                        Set<String> users = unifiedJedis.sunion(keys.toArray(new String[0]));
                        if (users != null && !users.isEmpty()) {
                            for (String user : users) {
                                try {
                                    setBuilder = setBuilder.add(UUID.fromString(user));
                                } catch (IllegalArgumentException ignored) {
                                }
                            }
                        }
                    }
                } catch (JedisConnectionException e) {
                    // Redis server has disappeared!
                    logFatal("Unable to get connection from pool - did your Redis server go away?");
                    throw new RuntimeException("Unable to get all players online", e);
                }
                return setBuilder.build();
            }
        }.execute();
    }
Espiiii-debug commented 5 months ago

i am not very good at explaining but if i get your question correctly, there is no need for using internal bungee stuff, basically uuid stuff is handled during login redis bungee does not change anything related to uuids, also when we sync the uuid which seen in https://github.com/ProxioDev/RedisBungee/blob/develop/RedisBungee-Bungee/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeBungeeListener.java see on join

note: i didn't create whole thing, i just made it modular in-case other proxy appears which in this case velocity

proxy init

proxy startup it starts 2 repeating tasks, heartbeat and integrity check

https://github.com/ProxioDev/RedisBungee/blob/develop/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/api/tasks/HeartbeatTask.java

heart beat keep adding/updating itself with timestamp in unix using redis server time stamps since you cannot trust that proxy is running with correct time settings, inside hashset

long redisTime = plugin.getRedisTime(unifiedJedis);
unifiedJedis.hset("heartbeats", plugin.getConfiguration().getProxyId(), String.valueOf(redisTime));

on join

when everything in the login event is passed like bans getting checked on login event then post login event get called and we call BungeePlayerUtils.createBungeePlayer(event.getPlayer(), unifiedJedis, true); and handle platform stuff like getting servername, which then method it self calls PlayerUtils.createPlayer(player.getUniqueId(), unifiedJedis, serverName, pendingConnection.getAddress().getAddress(), fireEvent); here is code for that:

public static void createPlayer(UUID uuid, UnifiedJedis unifiedJedis, String currentServer, InetAddress hostname, boolean fireEvent) {
        final boolean isKickedFromOtherLocation = isKickedOtherLocation(uuid.toString(), unifiedJedis);
        Map<String, String> playerData = new HashMap<>(4);
        playerData.put("online", "0");
        playerData.put("ip", hostname.getHostName());
        playerData.put("proxy", AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId());
        if (currentServer != null) {
            playerData.put("server", currentServer);
        }
        unifiedJedis.sadd("proxy:" + AbstractRedisBungeeAPI.getAbstractRedisBungeeAPI().getProxyId() + ":usersOnline", uuid.toString());
        unifiedJedis.hset("player:" + uuid, playerData);
        if (fireEvent && !isKickedFromOtherLocation) {
            playerJoinPayload(uuid, unifiedJedis, hostname);
        }
    }

you can see we are using redis hashsets to store stuff like hostnameip of the player etc.

but you can also see sadd which basically adds the player uuid into the set of online players, since each proxy got it own ID.

getting synced uuids

so to get the players uuids we need to get current heart beating proxies which can be seen below

here is the method of getting all players uuids from data is stored in redis

default Set<UUID> getPlayers() {
        return new RedisTask<Set<UUID>>(this) {
            @Override
            public Set<UUID> unifiedJedisTask(UnifiedJedis unifiedJedis) {
                ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
                try {
                    List<String> keys = new ArrayList<>();
                    for (String i : getProxiesIds()) {
                        keys.add("proxy:" + i + ":usersOnline");
                    }
                    if (!keys.isEmpty()) {
                        Set<String> users = unifiedJedis.sunion(keys.toArray(new String[0]));
                        if (users != null && !users.isEmpty()) {
                            for (String user : users) {
                                try {
                                    setBuilder = setBuilder.add(UUID.fromString(user));
                                } catch (IllegalArgumentException ignored) {
                                }
                            }
                        }
                    }
                } catch (JedisConnectionException e) {
                    // Redis server has disappeared!
                    logFatal("Unable to get connection from pool - did your Redis server go away?");
                    throw new RuntimeException("Unable to get all players online", e);
                }
                return setBuilder.build();
            }
        }.execute();
    }

You explained it to me very well, thank you!