diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java index 2dcc5cf..831fee3 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/DataManager.java @@ -6,6 +6,12 @@ */ package com.imaginarycode.minecraft.redisbungee; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent; +import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent; +import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -37,7 +43,7 @@ public class DataManager implements Listener { private final ConcurrentMap ipCache = new ConcurrentHashMap<>(192, 0.65f, 4); private final ConcurrentMap lastOnlineCache = new ConcurrentHashMap<>(192, 0.65f, 4); - static final UUID source = UUID.randomUUID(); + private final JsonParser parser = new JsonParser(); public String getServer(UUID uuid) { ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); @@ -216,22 +222,56 @@ public class DataManager implements Listener { if (!event.getChannel().equals("redisbungee-data")) return; - DataManagerMessage message = RedisBungee.getGson().fromJson(event.getMessage(), DataManagerMessage.class); + // Partially deserialize the message so we can look at the action + JsonObject jsonObject = parser.parse(event.getMessage()).getAsJsonObject(); - if (message.getSource().equals(source)) + String source = jsonObject.get("source").getAsString(); + + if (source.equals(plugin.getServerId())) return; - // For now we will just invalidate the caches, depending on what action occurred. - if (message.getAction() == DataManagerMessage.Action.SERVER_CHANGE) { - serverCache.remove(message.getTarget()); - } else { - invalidate(message.getTarget()); + DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString()); + + switch (action) { + case JOIN: + final DataManagerMessage message1 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken>() {}.getType()); + proxyCache.put(message1.getTarget(), message1.getSource()); + lastOnlineCache.put(message1.getTarget(), (long)0); + ipCache.put(message1.getTarget(), message1.getPayload().getAddress()); + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + plugin.getProxy().getPluginManager().callEvent(new PlayerJoinedNetworkEvent(message1.getTarget())); + } + }); + break; + case LEAVE: + final DataManagerMessage message2 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken>() {}.getType()); + invalidate(message2.getTarget()); + lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp()); + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + plugin.getProxy().getPluginManager().callEvent(new PlayerLeftNetworkEvent(message2.getTarget())); + } + }); + break; + case SERVER_CHANGE: + final DataManagerMessage message3 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken>() {}.getType()); + serverCache.put(message3.getTarget(), message3.getPayload().getServer()); + plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { + @Override + public void run() { + plugin.getProxy().getPluginManager().callEvent(new PlayerChangedServerNetworkEvent(message3.getTarget(), message3.getPayload().getServer())); + } + }); + break; } } @Getter @RequiredArgsConstructor - static class DataManagerMessage { + static class DataManagerMessage { enum Action { JOIN, LEAVE, @@ -239,7 +279,26 @@ public class DataManager implements Listener { } private final UUID target; - private final UUID source = DataManager.source; + private final String source = RedisBungee.getApi().getServerId(); private final Action action; // for future use! + private final T payload; + } + + @Getter + @RequiredArgsConstructor + static class LoginPayload { + private final InetAddress address; + } + + @Getter + @RequiredArgsConstructor + static class ServerChangePayload { + private final String server; + } + + @Getter + @RequiredArgsConstructor + static class LogoutPayload { + private final long timestamp; } } diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConsumer.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConsumer.java index 269dbcf..009c9cf 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConsumer.java +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeConsumer.java @@ -53,20 +53,27 @@ public class RedisBungeeConsumer implements Runnable { pipeline.hset("player:" + event1.getPlayer().getUniqueId().toString(), "ip", event1.getPlayer().getAddress().getAddress().getHostAddress()); plugin.getUuidTranslator().persistInfo(event1.getPlayer().getName(), event1.getPlayer().getUniqueId(), pipeline); pipeline.hset("player:" + event1.getPlayer().getUniqueId().toString(), "proxy", plugin.getServerId()); - pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage(event1.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.JOIN))); + pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( + event1.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.JOIN, + new DataManager.LoginPayload(event1.getPlayer().getAddress().getAddress())))); pipeline.sync(); } else if (event instanceof PlayerLoggedOffConsumerEvent) { PlayerLoggedOffConsumerEvent event1 = (PlayerLoggedOffConsumerEvent) event; Pipeline pipeline = jedis.pipelined(); - pipeline.hset("player:" + event1.getPlayer().getUniqueId().toString(), "online", String.valueOf(System.currentTimeMillis())); + long timestamp = System.currentTimeMillis(); + pipeline.hset("player:" + event1.getPlayer().getUniqueId().toString(), "online", String.valueOf(timestamp)); RedisUtil.cleanUpPlayer(event1.getPlayer().getUniqueId().toString(), pipeline); - pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage(event1.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.LEAVE))); + pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( + event1.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.LEAVE, + new DataManager.LogoutPayload(timestamp)))); pipeline.sync(); } else if (event instanceof PlayerChangedServerConsumerEvent) { PlayerChangedServerConsumerEvent event1 = (PlayerChangedServerConsumerEvent) event; Pipeline pipeline = jedis.pipelined(); pipeline.hset("player:" + event1.getPlayer().getUniqueId().toString(), "server", event1.getNewServer().getName()); - pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage(event1.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.SERVER_CHANGE))); + pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( + event1.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.SERVER_CHANGE, + new DataManager.ServerChangePayload(event1.getNewServer().getName())))); pipeline.sync(); } } diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java new file mode 100644 index 0000000..f497fa1 --- /dev/null +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerChangedServerNetworkEvent.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2013 tuxed + * This work is free. You can redistribute it and/or modify it under the + * terms of the Do What The Fuck You Want To Public License, Version 2, + * as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + */ +package com.imaginarycode.minecraft.redisbungee.events; + +import lombok.ToString; +import net.md_5.bungee.api.plugin.Event; + +import java.util.UUID; + +/** + * This event is sent when a player connects to a new server. RedisBungee sends the event only when + * the proxy the player has been connected to is different than the local proxy. + *

+ * This event corresponds to {@link net.md_5.bungee.api.event.ServerConnectedEvent}, and is fired + * asynchronously. + * + * @since 0.3.4 + */ +@ToString +public class PlayerChangedServerNetworkEvent extends Event { + private final UUID uuid; + private final String server; + + public PlayerChangedServerNetworkEvent(UUID uuid, String server) { + this.uuid = uuid; + this.server = server; + } + + public UUID getUuid() { + return uuid; + } + + public String getServer() { + return server; + } +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java new file mode 100644 index 0000000..ba30f4b --- /dev/null +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerJoinedNetworkEvent.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2013 tuxed + * This work is free. You can redistribute it and/or modify it under the + * terms of the Do What The Fuck You Want To Public License, Version 2, + * as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + */ +package com.imaginarycode.minecraft.redisbungee.events; + +import lombok.ToString; +import net.md_5.bungee.api.plugin.Event; + +import java.util.UUID; + +/** + * This event is sent when a player joins the network. RedisBungee sends the event only when + * the proxy the player has been connected to is different than the local proxy. + *

+ * This event corresponds to {@link net.md_5.bungee.api.event.PostLoginEvent}, and is fired + * asynchronously. + * + * @since 0.3.4 + */ +@ToString +public class PlayerJoinedNetworkEvent extends Event { + private final UUID uuid; + + public PlayerJoinedNetworkEvent(UUID uuid) { + this.uuid = uuid; + } + + public UUID getUuid() { + return uuid; + } +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java new file mode 100644 index 0000000..5694f4f --- /dev/null +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PlayerLeftNetworkEvent.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2013 tuxed + * This work is free. You can redistribute it and/or modify it under the + * terms of the Do What The Fuck You Want To Public License, Version 2, + * as published by Sam Hocevar. See http://www.wtfpl.net/ for more details. + */ +package com.imaginarycode.minecraft.redisbungee.events; + +import lombok.ToString; +import net.md_5.bungee.api.plugin.Event; + +import java.util.UUID; + +/** + * This event is sent when a player disconnects. RedisBungee sends the event only when + * the proxy the player has been connected to is different than the local proxy. + *

+ * This event corresponds to {@link net.md_5.bungee.api.event.PlayerDisconnectEvent}, and is fired + * asynchronously. + * + * @since 0.3.4 + */ +@ToString +public class PlayerLeftNetworkEvent extends Event { + private final UUID uuid; + + public PlayerLeftNetworkEvent(UUID uuid) { + this.uuid = uuid; + } + + public UUID getUuid() { + return uuid; + } +} diff --git a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java index 1d62651..d3a7eae 100644 --- a/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java +++ b/src/main/java/com/imaginarycode/minecraft/redisbungee/events/PubSubMessageEvent.java @@ -7,6 +7,7 @@ package com.imaginarycode.minecraft.redisbungee.events; import lombok.RequiredArgsConstructor; +import lombok.ToString; import net.md_5.bungee.api.plugin.Event; /** @@ -17,6 +18,7 @@ import net.md_5.bungee.api.plugin.Event; * @since 0.2.6 */ @RequiredArgsConstructor +@ToString public class PubSubMessageEvent extends Event { private final String channel; private final String message;