From 05150b9300d992deb009c202396ee21b86c3b81d Mon Sep 17 00:00:00 2001 From: Jonah Date: Wed, 4 Dec 2019 09:55:59 -0700 Subject: [PATCH 01/20] Small fixes --- .../jonahseguin/payload/PayloadPlugin.java | 4 +- .../com/jonahseguin/payload/base/Cache.java | 3 + .../payload/command/commands/CmdProfile.java | 8 +- .../database/InternalPayloadDatabase.java | 18 ++++ .../database/PayloadDatabaseService.java | 3 +- .../profile/PayloadProfileController.java | 96 +++++++++++-------- .../profile/listener/ProfileListener.java | 9 +- .../payload/server/PayloadServer.java | 2 + 8 files changed, 94 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/PayloadPlugin.java b/src/main/java/com/jonahseguin/payload/PayloadPlugin.java index 4c537db..d43d19c 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadPlugin.java +++ b/src/main/java/com/jonahseguin/payload/PayloadPlugin.java @@ -40,7 +40,7 @@ public class PayloadPlugin extends JavaPlugin { private static PayloadPlugin plugin = null; private Injector injector = null; private boolean locked = false; - private PayloadLocal local = new PayloadLocal(this); + private final PayloadLocal local = new PayloadLocal(this); private PCommandHandler commandHandler; private LangService lang; @@ -56,7 +56,7 @@ public static String format(String s, Object... args) { if (args.length > 0) { for (int i = 0; i < args.length; i++) { if (s.contains("{" + i + "}")) { - s = s.replace("{" + i + "}", args[i].toString()); + s = s.replace("{" + i + "}", args[i] != null ? args[i].toString() : "null"); } } } diff --git a/src/main/java/com/jonahseguin/payload/base/Cache.java b/src/main/java/com/jonahseguin/payload/base/Cache.java index f741f7b..ca64db7 100644 --- a/src/main/java/com/jonahseguin/payload/base/Cache.java +++ b/src/main/java/com/jonahseguin/payload/base/Cache.java @@ -8,6 +8,7 @@ import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.error.ErrorService; +import com.jonahseguin.payload.base.handshake.HandshakeService; import com.jonahseguin.payload.base.lang.LangService; import com.jonahseguin.payload.base.network.NetworkPayload; import com.jonahseguin.payload.base.network.NetworkService; @@ -153,5 +154,7 @@ public interface Cache, N extends NetworkPayload> ext N createNetworked(); + HandshakeService getHandshakeService(); + } diff --git a/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java b/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java index fce1d40..8362512 100644 --- a/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java +++ b/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java @@ -63,10 +63,10 @@ public void execute(CmdArgs args) { args.msg("&eNetwork Properties:"); args.msg("&7Online: &6{0}", (np.isOnline() ? "&aYes" : "&cNo")); args.msg("&7Loaded: &6{0}", (np.isLoaded() ? "&aYes" : "&cNo")); - args.msg("&7Last Seen On: &6{0}", np.getLastSeenServer()); - args.msg("&7Last Seen At: &6{0}", np.getLastSeen().toString()); - args.msg("&7Last Saved: &6{0}", np.getLastSaved().toString()); - args.msg("&7Last Cached: &6{0}", np.getLastCached().toString()); + args.msg("&7Last Seen On: &6{0}", np.getLastSeenServer() != null ? np.getLastSeenServer() : "&cN/A"); + args.msg("&7Last Seen At: &6{0}", np.getLastSeen() != null ? np.getLastSeen().toString() : "&cN/A"); + args.msg("&7Last Saved: &6{0}", np.getLastSaved() != null ? np.getLastSaved().toString() : "&cN/A"); + args.msg("&7Last Cached: &6{0}", np.getLastCached() != null ? np.getLastCached().toString() : "&cN/A"); } } else { args.msg("&cPayload: A profile with username '{0}' does not exist in cache '{1}'.", playerName); diff --git a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java b/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java index af01e87..29bddf7 100644 --- a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java +++ b/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java @@ -87,6 +87,15 @@ public boolean start() { server = serverService.start(); } running = true; + if (!mongo) { + payloadPlugin.getLogger().severe("Database " + name + ": Failed to start MongoDB"); + } + if (!redis) { + payloadPlugin.getLogger().severe("Database " + name + ": Failed to start Redis"); + } + if (!server) { + payloadPlugin.getLogger().severe("Database " + name + ": Failed to start Server Service"); + } return mongo && redis && server; } @@ -103,6 +112,15 @@ public boolean shutdown() { boolean mongo = this.disconnectMongo(); boolean redis = this.disconnectRedis(); this.running = false; + if (!mongo) { + payloadPlugin.getLogger().severe("Database " + name + ": Failed to shutdown MongoDB"); + } + if (!redis) { + payloadPlugin.getLogger().severe("Database " + name + ": Failed to shutdown Redis"); + } + if (!server) { + payloadPlugin.getLogger().severe("Database " + name + ": Failed to shutdown Server Service"); + } return mongo && redis && server; } diff --git a/src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java b/src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java index 003b1bc..8461170 100644 --- a/src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java +++ b/src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java @@ -31,7 +31,6 @@ public class PayloadDatabaseService implements DatabaseService { private final Morphia morphia; private final PayloadDatabase database; private Datastore datastore = null; - private ServerService serverService = null; @Inject public PayloadDatabaseService(Injector injector, @Database String name, @Database ErrorService error, PayloadDatabase database) { @@ -92,7 +91,7 @@ public boolean canFunction(@Nonnull DatabaseDependent dependent) { @Override public ServerService getServerService() { - return serverService; + return database.getServerService(); } @Override diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index cc708e3..11f29dc 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -85,58 +85,70 @@ public void uncache(@Nonnull X payload, boolean switchingServers) { if (cache.isCached(payload.getUniqueId())) { cache.uncache(payload.getUniqueId()); } - if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { - Optional o = cache.getNetworkService().get(payload.getUniqueId()); - if (o.isPresent()) { - NetworkProfile networkProfile = o.get(); - networkProfile.markUnloaded(switchingServers); - } + Optional o = cache.getNetworkService().get(payload.getUniqueId()); + if (o.isPresent()) { + NetworkProfile networkProfile = o.get(); + networkProfile.markUnloaded(switchingServers); } } private Optional cacheStandalone() { // Iterate each layer in order - if (cache.getLocalStore().has(uuid)) { - return cache.getLocalStore().get(uuid); + Optional localO = cache.getLocalStore().get(uuid); + if (localO.isPresent()) { + payload = localO.get(); } - Optional o = cache.getMongoStore().get(uuid); + else { + Optional o = cache.getMongoStore().get(uuid); - if (!o.isPresent()) { - // Failed to load from all layers + if (!o.isPresent()) { + // Failed to load from all layers - // If there was a failure/error, start failure handling instead of making a new profile - if (failure || !cache.getDatabase().getState().canCacheFunction(cache)) { - denyJoin = true; - joinDenyReason = ChatColor.RED + "The database is currently offline. Please try again soon."; - payload = null; - } else if (login) { - // Only make a new profile if they are logging in - getCache().getErrorService().debug("Creating a new profile for Payload " + username); - // Otherwise make a new profile - payload = cache.getInstantiator().instantiate(cache.getInjector()); - if (username != null) { - payload.setUsername(username); - } - payload.setUUID(uuid); - payload.setLoginIp(loginIp); - payload.setLoadingSource("New Profile"); - cache.getPool().submit(() -> cache.save(payload)); - } - // If they aren't logging in (getting a payload by UUID/username) and it wasn't found, return null as they don't exist. - } else { - payload = o.get(); - if (login) { - // Update their login ip - if (loginIp != null) { + // If there was a failure/error, start failure handling instead of making a new profile + if (failure || !cache.getDatabase().getState().canCacheFunction(cache)) { + denyJoin = true; + joinDenyReason = ChatColor.RED + "The database is currently offline. Please try again soon."; + payload = null; + } else if (login) { + // Only make a new profile if they are logging in + getCache().getErrorService().debug("Creating a new profile for Payload " + username); + // Otherwise make a new profile + payload = cache.getInstantiator().instantiate(cache.getInjector()); + if (username != null) { + payload.setUsername(username); + } + payload.setUUID(uuid); payload.setLoginIp(loginIp); + payload.setLoadingSource("New Profile"); + cache.getPool().submit(() -> cache.save(payload)); } - if (username != null) { - payload.setUsername(username); // Update their username - } + // If they aren't logging in (getting a payload by UUID/username) and it wasn't found, return null as they don't exist. + } else { + payload = o.get(); } + if (payload != null) { + if (login) { + + Optional oNP = cache.getNetworked(payload); + NetworkProfile networkProfile = oNP.orElseGet(() -> cache.getNetworkService().create(payload)); + + if (networkProfile != null) { + networkProfile.markLoaded(login); + cache.runAsync(() -> cache.getNetworkService().save(networkProfile)); + } + + // Update their login ip + if (loginIp != null) { + payload.setLoginIp(loginIp); + } + if (username != null) { + payload.setUsername(username); // Update their username + } + } - // Cache the Payload if successful - cache.cache(payload); + // Cache the Payload if successful + cache.cache(payload); + } } return Optional.ofNullable(payload); @@ -239,8 +251,12 @@ private Optional cacheNetworkNode() { public void initializeOnJoin(Player player) { this.player = player; if (payload != null) { + cache.getErrorService().debug("called initializeOnJoin() in controller for " + player.getName()); payload.initializePlayer(player); } + else { + cache.getErrorService().debug("failed to call initializeOnJoin() for " + player.getName() + " (payload is null in controller)"); + } } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index f0fa7a7..7b273fd 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -64,7 +64,7 @@ public void onProfileCachingInit(PlayerJoinEvent event) { PayloadProfileCache cache = (PayloadProfileCache) c; PayloadProfileController controller = cache.getController(player.getUniqueId()); if (controller != null) { - cache.getErrorService().debug("Initializing player " + player.getName()); + cache.getErrorService().debug("Initializing player " + player.getName() + " for cache " + cache.getName()); controller.initializeOnJoin(player); } else { @@ -91,6 +91,13 @@ public void onProfileQuit(final PlayerQuitEvent event) { PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + Optional oNP = cache.getNetworked(profile); + oNP.ifPresent(networkProfile -> { + networkProfile.setOnline(false); + networkProfile.setLastSeen(new Date()); + cache.runAsync(() -> cache.getNetworkService().save(networkProfile)); + }); + profile.uninitializePlayer(); if (!cache.save(profile)) { cache.getErrorService().capture("Error saving profile on quit: " + player.getName()); diff --git a/src/main/java/com/jonahseguin/payload/server/PayloadServer.java b/src/main/java/com/jonahseguin/payload/server/PayloadServer.java index e34409b..5c46c34 100644 --- a/src/main/java/com/jonahseguin/payload/server/PayloadServer.java +++ b/src/main/java/com/jonahseguin/payload/server/PayloadServer.java @@ -5,6 +5,7 @@ package com.jonahseguin.payload.server; +import com.google.inject.Inject; import dev.morphia.annotations.Embedded; import lombok.Getter; import lombok.Setter; @@ -18,6 +19,7 @@ public class PayloadServer { private long lastPing = 0; private boolean online = false; + @Inject public PayloadServer() { } From f07eb641c1ecae59e9530ea3ca39fd7f85a27f5e Mon Sep 17 00:00:00 2001 From: Jonah Date: Fri, 6 Dec 2019 14:36:16 -0700 Subject: [PATCH 02/20] Fixes to termination saving and some debug messages + other small improvements (always use Network Payloads) --- .../payload/base/PayloadCache.java | 1 + .../mode/object/PayloadObjectCache.java | 12 +++- .../mode/profile/PayloadProfileCache.java | 29 ++++---- .../profile/PayloadProfileController.java | 8 ++- .../payload/server/PayloadServerService.java | 68 +++++++++++-------- 5 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java index 48f203b..15d7f7b 100644 --- a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java +++ b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java @@ -353,6 +353,7 @@ public boolean save(@Nonnull X payload) { } return true; } + errorService.capture("Failed to save payload " + keyToString(payload.getIdentifier()) + " (during saveNoSync())"); return false; } diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java index 25c9dfb..1b78505 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java @@ -65,6 +65,13 @@ protected boolean initialize() { @Override protected boolean terminate() { + getCached().forEach(payload -> { + getNetworked(payload).ifPresent(no -> { + if (no.isThisMostRelevantServer()) { + save(payload); + } + }); + }); controllers.clear(); return true; } @@ -80,18 +87,21 @@ public PayloadObjectController controller(@Nonnull String key) { public boolean saveNoSync(@Nonnull X payload) { boolean success = true; if (!localStore.save(payload)) { + errorService.capture("Failed to save to local store for object " + payload.getIdentifier()); success = false; } if (!mongoStore.save(payload)) { + errorService.capture("Failed to save to MongoDB store for object " + payload.getIdentifier()); success = false; } - if (success && mode.equals(PayloadMode.NETWORK_NODE)) { + if (success) { Optional o = networkService.get(payload); if (o.isPresent()) { NetworkObject no = o.get(); no.markSaved(); if (!networkService.save(no)) { success = false; + errorService.capture("Failed to save Network Object for object " + payload.getIdentifier()); } } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index 67125ab..339aa66 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -90,7 +90,7 @@ protected boolean initialize() { protected boolean terminate() { boolean success = true; for (Player player : plugin.getServer().getOnlinePlayers()) { - player.kickPlayer(lang.module(this).format("shutdown")); + getFromCache(player).ifPresent(this::save); } controllers.clear(); if (!localStore.shutdown()) { @@ -332,28 +332,27 @@ public Future saveAsync(@Nonnull X payload) { @Override public boolean save(@Nonnull X payload) { Preconditions.checkNotNull(payload); - if (mode.equals(PayloadMode.NETWORK_NODE)) { - Optional onp = networkService.get(payload); - if (onp.isPresent()) { - NetworkProfile np = onp.get(); - if (this.saveNoSync(payload)) { - np.markSaved(); - if (networkService.save(np)) { - if (settings.isEnableSync()) { - sync.update(payload.getIdentifier()); - } - return true; - } else { - return false; + Optional onp = networkService.get(payload); + if (onp.isPresent()) { + NetworkProfile np = onp.get(); + if (this.saveNoSync(payload)) { + np.markSaved(); + if (networkService.save(np)) { + if (settings.isEnableSync()) { + sync.update(payload.getIdentifier()); } + return true; } else { + errorService.capture("Failed to save profile " + payload.getName() + ": Couldn't save network profile (but saved normal profile)"); return false; } } else { + errorService.capture("Failed to save profile " + payload.getName() + ": Failed to save to database (via saveNoSync())"); return false; } } else { - return saveNoSync(payload); + errorService.capture("Failed to save profile " + payload.getName() + ": Network Profile doesn't exist"); + return false; } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index 11f29dc..8600847 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -233,7 +233,7 @@ private Optional cacheNetworkNode() { } } - if (payload != null && !loadedFromLocal) { + if (payload != null) { if (username != null && !payload.getUsername().equalsIgnoreCase(username)) { cache.getErrorService().debug("Updated username: " + payload.getUsername() + " to " + username); payload.setUsername(username); @@ -241,8 +241,10 @@ private Optional cacheNetworkNode() { cache.getErrorService().capture("Error saving Payload during caching after username update: " + payload.getUsername()); } } - if (login || cache.getSettings().isAlwaysCacheOnLoadNetworkNode()) { - cache.cache(payload); + if (!loadedFromLocal) { + if (login || cache.getSettings().isAlwaysCacheOnLoadNetworkNode()) { + cache.cache(payload); + } } } return Optional.ofNullable(payload); diff --git a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java index b028033..7216988 100644 --- a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java +++ b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java @@ -53,19 +53,24 @@ public PayloadServerService(DatabaseService database, PayloadPlugin payloadPlugi @Override public boolean start() { - this.publisher = new ServerPublisher(this); - - this.executorService.submit(() -> { - this.jedisSubscriber = this.database.getJedisResource(); - this.subscriber = new ServerSubscriber(this); - this.jedisSubscriber.subscribe(this.subscriber, - "server-join", "server-ping", "server-quit", "server-update-name"); - }); - - this.publisher.publishJoin(); - this.pingTask = payloadPlugin.getServer().getScheduler().runTaskTimerAsynchronously(payloadPlugin, this, (PING_FREQUENCY_SECONDS * 20), (PING_FREQUENCY_SECONDS * 20)); - running = true; - return true; + try { + this.publisher = new ServerPublisher(this); + + this.executorService.submit(() -> { + this.jedisSubscriber = this.database.getJedisResource(); + this.subscriber = new ServerSubscriber(this); + this.jedisSubscriber.subscribe(this.subscriber, + "server-join", "server-ping", "server-quit", "server-update-name"); + }); + + this.publisher.publishJoin(); + this.pingTask = payloadPlugin.getServer().getScheduler().runTaskTimerAsynchronously(payloadPlugin, this, (PING_FREQUENCY_SECONDS * 20), (PING_FREQUENCY_SECONDS * 20)); + running = true; + return true; + } catch (Exception ex) { + error.capture(ex, "Error starting Server Service for database: " + name); + return false; + } } @Override @@ -137,27 +142,32 @@ private void shutdownExecutor() { @Override public boolean shutdown() { - if (this.pingTask != null) { - this.pingTask.cancel(); - } + try { + if (this.pingTask != null) { + this.pingTask.cancel(); + } - if (this.subscriber != null) { - if (this.subscriber.isSubscribed()) { - this.subscriber.unsubscribe(); + if (this.subscriber != null) { + if (this.subscriber.isSubscribed()) { + this.subscriber.unsubscribe(); + } + this.subscriber = null; } - this.subscriber = null; - } - this.publisher.publishQuit(); // Sync. - this.shutdownExecutor(); + this.publisher.publishQuit(); // Sync. + this.shutdownExecutor(); - this.publisher = null; - if (this.jedisSubscriber != null) { - this.jedisSubscriber.close(); - this.jedisSubscriber = null; + this.publisher = null; + if (this.jedisSubscriber != null) { + this.jedisSubscriber.close(); + this.jedisSubscriber = null; + } + running = false; + return true; + } catch (Exception ex) { + error.capture("Error shutting down Server Service for database: " + name); + return false; } - running = false; - return true; } @Override From 198728fa6621e09728687a08c3e8630374114980 Mon Sep 17 00:00:00 2001 From: Jonah Date: Mon, 9 Dec 2019 21:39:07 -0700 Subject: [PATCH 03/20] Final stuff --- .../java/com/jonahseguin/payload/base/sync/SyncHandshake.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java b/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java index 08525c4..607d17d 100644 --- a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java +++ b/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java @@ -79,7 +79,7 @@ public void receive() { @Override public boolean shouldAccept() { - return cache.isCached(identifier); + return cache.isCached(identifier) || cache.getSyncMode().equals(SyncMode.ALWAYS); } @Override From 4d41ddd1a64c6926300bf1fc9ef234feeda20931 Mon Sep 17 00:00:00 2001 From: Jonah Date: Wed, 18 Dec 2019 14:40:40 -0700 Subject: [PATCH 04/20] Major fixes to handshake system and profile loading/networking --- .../com/jonahseguin/payload/PayloadAPI.java | 4 +- .../com/jonahseguin/payload/base/Cache.java | 3 + .../payload/base/handshake/Handshake.java | 9 +- .../payload/base/handshake/HandshakeData.java | 9 +- .../base/handshake/HandshakeListener.java | 8 +- .../base/handshake/HandshakeService.java | 3 + .../handshake/PayloadHandshakeService.java | 12 ++- .../payload/base/sync/CacheSyncService.java | 10 +- .../payload/base/sync/SyncHandshake.java | 9 +- .../database/InternalPayloadDatabase.java | 3 + .../payload/mode/object/ObjectHandshake.java | 10 +- .../payload/mode/object/PayloadObject.java | 8 -- .../mode/object/PayloadObjectCache.java | 2 +- .../mode/object/PayloadObjectController.java | 2 +- .../mode/profile/PayloadProfileCache.java | 2 +- .../profile/PayloadProfileController.java | 93 +++++++++++-------- .../mode/profile/ProfileHandshake.java | 34 +++++-- .../profile/listener/ProfileListener.java | 7 +- .../payload/server/PayloadServerService.java | 2 +- 19 files changed, 141 insertions(+), 89 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/PayloadAPI.java b/src/main/java/com/jonahseguin/payload/PayloadAPI.java index 49d281a..7e449d1 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadAPI.java +++ b/src/main/java/com/jonahseguin/payload/PayloadAPI.java @@ -178,9 +178,7 @@ public void setPayloadID(String name) { for (Cache cache : getCaches().values()) { cache.updatePayloadID(); } - for (PayloadDatabase database : getDatabases().values()) { - database.getServerService().getPublisher().publishUpdateName(oldName, name); - } + serverServices.values().forEach(s -> s.getPublisher().publishUpdateName(oldName, name)); } else { throw new IllegalArgumentException("Payload ID must be alphanumeric, '" + name + "' is not valid."); } diff --git a/src/main/java/com/jonahseguin/payload/base/Cache.java b/src/main/java/com/jonahseguin/payload/base/Cache.java index ca64db7..73a5268 100644 --- a/src/main/java/com/jonahseguin/payload/base/Cache.java +++ b/src/main/java/com/jonahseguin/payload/base/Cache.java @@ -5,6 +5,7 @@ package com.jonahseguin.payload.base; +import com.google.inject.Injector; import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.error.ErrorService; @@ -156,5 +157,7 @@ public interface Cache, N extends NetworkPayload> ext HandshakeService getHandshakeService(); + Injector getInjector(); + } diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java b/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java index f1c6403..8696243 100644 --- a/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java +++ b/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java @@ -6,6 +6,7 @@ package com.jonahseguin.payload.base.handshake; import com.google.inject.Inject; +import com.google.inject.Injector; import com.jonahseguin.payload.database.DatabaseService; import lombok.Getter; import redis.clients.jedis.Jedis; @@ -15,6 +16,7 @@ @Getter public abstract class Handshake { + protected final Injector injector; @Inject protected HandshakeService handshakeService; @Inject @@ -23,10 +25,12 @@ public abstract class Handshake { protected HandshakeHandler handler; @Inject - public Handshake() { - // No-args constructor required by Guice to create an instance + public Handshake(Injector injector) { + this.injector = injector; + injector.injectMembers(this); } + public abstract String channelPublish(); public abstract String channelReply(); @@ -63,6 +67,7 @@ public void listen() { subscriber.subscribe(listener, channelPublish(), channelReply()); } catch (Exception ex) { database.getErrorService().capture(ex, "Error during listening for handshake " + this.getClass().getSimpleName()); + ex.printStackTrace(); } } diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java index 35054c8..f3e76fd 100644 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java +++ b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + package com.jonahseguin.payload.base.handshake; import lombok.Data; @@ -20,10 +25,6 @@ public void writeID() { document.append(ID, UUID.randomUUID().toString()); } - public void writeChannel() { - - } - public Document append(String key, Object value) { return document.append(key, value); } diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java index 3d055ff..c34ed5d 100644 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java +++ b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + package com.jonahseguin.payload.base.handshake; import org.bson.Document; @@ -20,8 +25,7 @@ public void onMessage(String channel, String json) { service.receive(controller.channelPublish(), mapData(json)); } else if (channel.equalsIgnoreCase(controller.channelReply())) { // Receiving handshake reply - service.receive(controller.channelReply(), mapData(json)); - + service.receiveReply(controller.channelReply(), mapData(json)); } } diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java index 8f87efb..10e7742 100644 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java +++ b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java @@ -6,6 +6,7 @@ package com.jonahseguin.payload.base.handshake; import com.jonahseguin.payload.base.Service; +import com.jonahseguin.payload.database.DatabaseService; import javax.annotation.Nonnull; @@ -22,4 +23,6 @@ public interface HandshakeService extends Service { @Nonnull String getName(); + DatabaseService getDatabaseService(); + } diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java b/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java index 98aa6a7..f2627b3 100644 --- a/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java +++ b/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java @@ -40,6 +40,11 @@ public boolean isRunning() { return running; } + @Override + public DatabaseService getDatabaseService() { + return database; + } + @Override public boolean shutdown() { this.containers.values().stream() @@ -107,10 +112,9 @@ public void receive(@Nonnull String channel, @Nonnull HandshakeData data) { public void subscribe(@Nonnull H subscriber) { Preconditions.checkNotNull(subscriber); HandshakeContainer container = new HandshakeContainer(subscriber); - Handshake controller = container.getSubscriberController(); - containers.put(controller.channelPublish(), container); - containers.put(controller.channelReply(), container); - executor.submit(controller::listen); + containers.put(subscriber.channelPublish(), container); + containers.put(subscriber.channelReply(), container); + executor.submit(subscriber::listen); } @Override diff --git a/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java b/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java index ed70eeb..6fbabec 100644 --- a/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java +++ b/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java @@ -15,7 +15,7 @@ import javax.annotation.Nonnull; import java.util.Optional; -public class CacheSyncService, N extends NetworkPayload, D> implements SyncService { +public class CacheSyncService, N extends NetworkPayload> implements SyncService { private final Cache cache; private final HandshakeService handshakeService; @@ -36,7 +36,7 @@ public void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback(cache, payload.getIdentifier(), SyncHandshakeMode.UPDATE)).afterReply(h -> { + handshakeService.publish(new SyncHandshake<>(cache.getInjector(), cache, payload.getIdentifier(), SyncHandshakeMode.UPDATE)).afterReply(h -> { Optional ox = cache.getFromDatabase(payload.getIdentifier()); callback.callback(ox); }); @@ -49,19 +49,19 @@ public void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback(cache, key, SyncHandshakeMode.UPDATE)); + handshakeService.publish(new SyncHandshake<>(cache.getInjector(), cache, key, SyncHandshakeMode.UPDATE)); } @Override public void uncache(@Nonnull K key) { Preconditions.checkNotNull(key); - handshakeService.publish(new SyncHandshake<>(cache, key, SyncHandshakeMode.UNCACHE)); + handshakeService.publish(new SyncHandshake<>(cache.getInjector(), cache, key, SyncHandshakeMode.UNCACHE)); } @Override public boolean start() { running = true; - handshakeService.subscribe(new SyncHandshake<>(cache)); + handshakeService.subscribe(new SyncHandshake<>(cache.getInjector(), cache)); return true; } diff --git a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java b/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java index 607d17d..d155e6f 100644 --- a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java +++ b/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java @@ -6,6 +6,7 @@ package com.jonahseguin.payload.base.sync; import com.google.common.base.Preconditions; +import com.google.inject.Injector; import com.jonahseguin.payload.base.Cache; import com.jonahseguin.payload.base.handshake.Handshake; import com.jonahseguin.payload.base.handshake.HandshakeData; @@ -26,11 +27,13 @@ public class SyncHandshake, N extends NetworkPayload> private K identifier; private SyncHandshakeMode mode; - public SyncHandshake(Cache cache) { + public SyncHandshake(Injector injector, Cache cache) { + super(injector); this.cache = cache; } - public SyncHandshake(Cache cache, @Nonnull K identifier, @Nonnull SyncHandshakeMode mode) { + public SyncHandshake(Injector injector, Cache cache, @Nonnull K identifier, @Nonnull SyncHandshakeMode mode) { + super(injector); Preconditions.checkNotNull(identifier); this.cache = cache; this.identifier = identifier; @@ -39,7 +42,7 @@ public SyncHandshake(Cache cache, @Nonnull K identifier, @Nonnull SyncH @Override public SyncHandshake create() { - return new SyncHandshake<>(cache); + return new SyncHandshake<>(injector, cache); } @Override diff --git a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java b/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java index 29bddf7..4dfd0d9 100644 --- a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java +++ b/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java @@ -76,6 +76,7 @@ public InternalPayloadDatabase(PayloadPlugin payloadPlugin, @Database ErrorServi @Override public boolean start() { Preconditions.checkState(!running, "Database " + name + " is already running"); + errorService.debug("Starting database " + name); fromConfigFile("database.yml"); boolean mongo = this.connectMongo(); boolean redis = this.connectRedis(); @@ -96,6 +97,7 @@ public boolean start() { if (!server) { payloadPlugin.getLogger().severe("Database " + name + ": Failed to start Server Service"); } + errorService.debug("Started database " + name); return mongo && redis && server; } @@ -121,6 +123,7 @@ public boolean shutdown() { if (!server) { payloadPlugin.getLogger().severe("Database " + name + ": Failed to shutdown Server Service"); } + errorService.debug("Shutdown database " + name); return mongo && redis && server; } diff --git a/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java b/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java index 3c9d4d9..505d88c 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java @@ -6,6 +6,7 @@ package com.jonahseguin.payload.mode.object; import com.google.common.base.Preconditions; +import com.google.inject.Injector; import com.jonahseguin.payload.base.handshake.Handshake; import com.jonahseguin.payload.base.handshake.HandshakeData; @@ -18,20 +19,21 @@ public class ObjectHandshake extends Handshake { private final ObjectCache cache; private String identifier = null; - public ObjectHandshake(@Nonnull ObjectCache cache) { + public ObjectHandshake(Injector injector, @Nonnull ObjectCache cache) { + super(injector); Preconditions.checkNotNull(cache); this.cache = cache; } - public ObjectHandshake(@Nonnull ObjectCache cache, @Nonnull String identifier) { - this(cache); + public ObjectHandshake(Injector injector, @Nonnull ObjectCache cache, @Nonnull String identifier) { + this(injector, cache); Preconditions.checkNotNull(identifier); this.identifier = identifier; } @Override public ObjectHandshake create() { - return new ObjectHandshake(cache); + return new ObjectHandshake(injector, cache); } @Override diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java index d21677e..752ecf4 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java @@ -32,14 +32,6 @@ public PayloadObject(ObjectCache cache) { if (cache != null && cache.getApi().getPayloadID() != null) { this.payloadId = cache.getApi().getPayloadID(); } - if (cache == null) { - System.out.println("cache is null"); - } - if (cache != null) { - if (cache.getApi().getPayloadID() == null) { - System.out.println("payload id is null"); - } - } } @Override diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java index 1b78505..34a4e9f 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java @@ -57,7 +57,7 @@ protected boolean initialize() { } } if (mode.equals(PayloadMode.NETWORK_NODE)) { - handshakeService.subscribe(new ObjectHandshake(this)); + handshakeService.subscribe(new ObjectHandshake(injector, this)); } database.getMorphia().map(NetworkObject.class); return success; diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java index 0b52fd6..5069f63 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java @@ -59,7 +59,7 @@ public Optional cache() { load(true); } else { // Handshake - HandshakeHandler h = cache.getHandshakeService().publish(new ObjectHandshake(cache, identifier)); + HandshakeHandler h = cache.getHandshakeService().publish(new ObjectHandshake(cache.getInjector(), cache, identifier)); h.waitForReply(cache.getSettings().getHandshakeTimeoutSeconds()); load(false); } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index 339aa66..b65d5b6 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -80,7 +80,7 @@ protected boolean initialize() { errorService.capture("Failed to start MongoDB store for cache " + name); } if (mode.equals(PayloadMode.NETWORK_NODE)) { - handshakeService.subscribe(new ProfileHandshake(this)); + handshakeService.subscribe(new ProfileHandshake(injector, this)); } database.getMorphia().map(NetworkProfile.class); return success; diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index 8600847..277213f 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -82,13 +82,16 @@ public Optional cache() { @Override public void uncache(@Nonnull X payload, boolean switchingServers) { - if (cache.isCached(payload.getUniqueId())) { - cache.uncache(payload.getUniqueId()); + if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { + if (cache.isCached(payload.getUniqueId())) { + cache.uncache(payload.getUniqueId()); + } } Optional o = cache.getNetworkService().get(payload.getUniqueId()); if (o.isPresent()) { NetworkProfile networkProfile = o.get(); networkProfile.markUnloaded(switchingServers); + cache.runAsync(() -> cache.getNetworkService().save(networkProfile)); } } @@ -187,49 +190,52 @@ private void load(boolean local) { } private Optional cacheNetworkNode() { - Player player = cache.getPlugin().getServer().getPlayer(uuid); - if (player != null && player.isOnline()) { - load(true); - } else { - cache.getErrorService().debug("Starting caching Payload [network-node] " + uuid.toString() + "(login: " + login + ")"); - Optional oNP = cache.getNetworkService().get(uuid); - NetworkProfile networkProfile = null; - if (oNP.isPresent()) { - networkProfile = oNP.get(); - if (networkProfile.isOnlineOtherServer()) { - PayloadServer server = cache.getServerService().get(networkProfile.getLastSeenServer()).orElse(null); - if (server != null && server.isOnline()) { - // Handshake - HandshakeHandler handshake = cache.getHandshakeService().publish(new ProfileHandshake(cache, uuid)); - Optional o = handshake.waitForReply(cache.getSettings().getHandshakeTimeoutSeconds()); - if (o.isPresent()) { - load(false); - } else { - // Timed out - denyJoin = true; - joinDenyReason = ChatColor.RED + "Timed out while loading your profile. Please try again."; - } - } else { - // Target server isn't online + if (!login) { + Player player = cache.getPlugin().getServer().getPlayer(uuid); + if (player != null && player.isOnline()) { + load(true); + // Just getting them from the cache when they are online, skip all the other shit and just return this. + // For performance :) + if (payload != null) { + return Optional.of(payload); + } + } + } + + NetworkProfile networkProfile = cache.getNetworkService().get(uuid).orElse(null); + cache.getErrorService().debug("Caching Payload [network-node] " + uuid.toString() + " (login: " + login + ")"); + if (networkProfile != null) { + if (networkProfile.isOnlineOtherServer()) { + PayloadServer server = cache.getServerService().get(networkProfile.getLastSeenServer()).orElse(null); + if (server != null && server.isOnline()) { + // Handshake + cache.getErrorService().debug("Handshaking " + uuid.toString() + " from server " + server.getName()); + HandshakeHandler handshake = cache.getHandshakeService().publish(new ProfileHandshake(cache.getInjector(), cache, uuid, server.getName())); + Optional o = handshake.waitForReply(cache.getSettings().getHandshakeTimeoutSeconds()); + if (o.isPresent()) { + cache.getErrorService().debug("Handshake complete for " + uuid.toString() + ", loading from DB"); load(false); + } else { + // Timed out + denyJoin = true; + joinDenyReason = ChatColor.RED + "Timed out while loading your profile. Please try again."; + cache.getErrorService().debug("Handshake timed out for " + uuid.toString()); } } else { - load(networkProfile.isOnlineThisServer()); // only load from local if they're online this server + // Target server isn't online, or there is no recent server + cache.getErrorService().debug("Target server '" + (server != null ? server.getName() : "n/a") + "' not online for handshake for " + uuid.toString(), ", loading from database"); + load(false); } } else { - // Create the network profile - load(true); - if (payload != null) { - if (login) { - networkProfile = cache.getNetworkService().create(payload); - } - } + load(networkProfile.isOnlineThisServer()); // only load from local if they're online this server } - - if (networkProfile != null) { - networkProfile.markLoaded(login); - NetworkProfile finalNetworkProfile = networkProfile; - cache.runAsync(() -> cache.getNetworkService().save(finalNetworkProfile)); + } else { + // Create the network profile + load(true); + if (payload != null) { + if (login) { + networkProfile = cache.getNetworkService().create(payload); + } } } @@ -246,6 +252,15 @@ private Optional cacheNetworkNode() { cache.cache(payload); } } + if (login) { + if (networkProfile == null) { + networkProfile = cache.getNetworkService().create(payload); + } + + networkProfile.markLoaded(true); + NetworkProfile finalNetworkProfile = networkProfile; + cache.runAsync(() -> cache.getNetworkService().save(finalNetworkProfile)); + } } return Optional.ofNullable(payload); } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java index d4a06f1..f2257c2 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java @@ -6,6 +6,7 @@ package com.jonahseguin.payload.mode.profile; import com.google.common.base.Preconditions; +import com.google.inject.Injector; import com.jonahseguin.payload.base.handshake.Handshake; import com.jonahseguin.payload.base.handshake.HandshakeData; @@ -16,23 +17,27 @@ public class ProfileHandshake extends Handshake { public static final String KEY_UUID = "uuid"; + public static final String KEY_SERVER = "targetServer"; private final ProfileCache cache; private UUID uuid = null; + private String targetServer = null; - public ProfileHandshake(@Nonnull ProfileCache cache) { + public ProfileHandshake(Injector injector, @Nonnull ProfileCache cache) { + super(injector); Preconditions.checkNotNull(cache); this.cache = cache; } - public ProfileHandshake(@Nonnull ProfileCache cache, @Nonnull UUID uuid) { - this(cache); + public ProfileHandshake(Injector injector, @Nonnull ProfileCache cache, @Nonnull UUID uuid, @Nonnull String targetServer) { + this(injector, cache); Preconditions.checkNotNull(uuid); this.uuid = uuid; + this.targetServer = targetServer; } @Override public ProfileHandshake create() { - return new ProfileHandshake(cache); + return new ProfileHandshake(injector, cache); } @Override @@ -47,12 +52,22 @@ public String channelReply() { @Override public void load(@Nonnull HandshakeData data) { - this.uuid = UUID.fromString(data.getDocument().getString(KEY_UUID)); + try { + this.uuid = UUID.fromString(data.getDocument().getString(KEY_UUID)); + this.targetServer = data.getDocument().getString(KEY_SERVER); + } catch (Exception ex) { + ex.printStackTrace(); + } } @Override public void write(@Nonnull HandshakeData data) { - data.append(KEY_UUID, this.uuid); + try { + data.append(KEY_UUID, this.uuid.toString()); + data.append(KEY_SERVER, this.targetServer); + } catch (Exception ex) { + ex.printStackTrace(); + } } @Override @@ -67,9 +82,14 @@ public void receive() { } } + @Override + public boolean shouldReply() { + return true; + } + @Override public boolean shouldAccept() { Optional o = cache.getLocalStore().get(uuid); - return o.isPresent() && o.get().isOnline(); + return targetServer.equalsIgnoreCase(cache.getDatabase().getServerService().getThisServer().getName()) && o.isPresent() && o.get().isOnline(); } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index 7b273fd..9f1b67f 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -119,10 +119,7 @@ public void onProfileQuit(final PlayerQuitEvent event) { // Not switching servers (no incoming handshake) -- we can assume they are actually // Logging out, and not switching servers - networkProfile.setOnline(false); - networkProfile.setLastSeen(new Date()); - - cache.getNetworkService().save(networkProfile); + cache.controller(event.getPlayer().getUniqueId()).uncache(profile, false); cache.save(profile); // Don't publish a sync since we're switching servers // In network node mode, join is handled before quit when switching servers // so we don't want to save on quit @@ -135,6 +132,8 @@ public void onProfileQuit(final PlayerQuitEvent event) { PayloadProfileSwitchServersEvent payloadEvent = new PayloadProfileSwitchServersEvent(profile); cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); + // Switching servers, don't save their data -- just remove cache.getLocalStore().remove(player.getUniqueId()); // remove on quit to prevent accidental data rollbacks cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); diff --git a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java index 7216988..28b2daa 100644 --- a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java +++ b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java @@ -91,7 +91,7 @@ public boolean has(@Nonnull String name) { @Override public Optional get(@Nonnull String name) { Preconditions.checkNotNull(name); - return Optional.of(this.servers.get(name.toLowerCase())); + return Optional.ofNullable(this.servers.get(name.toLowerCase())); } void handlePing(@Nonnull String serverName) { From 7a3ac7c737cab8904884bb945f43ef7bcc117f7e Mon Sep 17 00:00:00 2001 From: Jonah Date: Fri, 20 Dec 2019 01:17:46 -0700 Subject: [PATCH 05/20] Add handshake timeout threshold bypass --- .../jonahseguin/payload/PayloadPlugin.java | 28 ++-------- .../payload/base/PayloadCache.java | 7 --- .../mode/object/PayloadObjectCache.java | 53 ++++++++++++++++--- .../mode/profile/PayloadProfileCache.java | 13 ++++- .../profile/PayloadProfileController.java | 17 ++++-- .../profile/listener/ProfileListener.java | 10 +--- 6 files changed, 76 insertions(+), 52 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/PayloadPlugin.java b/src/main/java/com/jonahseguin/payload/PayloadPlugin.java index d43d19c..749a9a8 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadPlugin.java +++ b/src/main/java/com/jonahseguin/payload/PayloadPlugin.java @@ -17,12 +17,10 @@ import com.jonahseguin.payload.mode.profile.listener.ProfileListener; import lombok.Getter; import org.bukkit.ChatColor; -import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; import java.io.IOException; -import java.net.InetAddress; /** * Created by Jonah on 11/16/2017. @@ -96,6 +94,8 @@ public void onEnable() { injector = Guice.createInjector(Stage.PRODUCTION, PayloadAPI.install(this, "PayloadDatabase")); lang = new PayloadLangService(this); + lang.lang().load(); + lang.lang().save(); commandHandler = new PCommandHandler(this, lang, injector); this.getServer().getPluginManager().registerEvents(injector.getInstance(LockListener.class), this); @@ -104,24 +104,6 @@ public void onEnable() { this.getLogger().info(PayloadPlugin.format("Payload v{0} by Jonah Seguin enabled.", getDescription().getVersion())); } - /** - * Get the simple IP address from an {@link InetAddress} - * @param inetAddress The InetAddress - * @return The simple IP in {@link String} form - */ - public static String getIP(InetAddress inetAddress) { - return inetAddress.toString().split("/")[1]; - } - - /** - * Run a task async. via Bukkit scheduler - * @param plugin {@link JavaPlugin} to run via - * @param runnable What to run - */ - public static void runASync(Plugin plugin, Runnable runnable) { - plugin.getServer().getScheduler().runTaskAsynchronously(plugin, runnable); - } - /** * Whether to globally lock the server from players joining that don't have the bypass permission * @return Locked @@ -157,6 +139,8 @@ public boolean isDebug() { @Override public void onDisable() { + lang.lang().load(); + lang.lang().save(); this.getLogger().info(PayloadPlugin.format("Payload v{0} by Jonah Seguin disabled.", getDescription().getVersion())); plugin = null; } @@ -171,10 +155,6 @@ public PCommandHandler getCommandHandler() { return commandHandler; } - public ClassLoader getPayloadClassLoader() { - return this.getClassLoader(); - } - public void setDebug(boolean debug) { this.local.setDebug(debug); this.local.getConfig().set("debug", debug); diff --git a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java index 15d7f7b..cd229ab 100644 --- a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java +++ b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java @@ -147,9 +147,6 @@ public final boolean start() { */ public final boolean shutdown() { Preconditions.checkState(running, "Cache " + name + " is not running!"); - - int failedSaves = saveAll(); // First, save everything. - boolean success = true; if (!terminate()) { @@ -170,10 +167,6 @@ public final boolean shutdown() { } shutdownPool(); running = false; - if (failedSaves > 0) { - errorService.capture(failedSaves + " Payload objects failed to save during shutdown"); - success = false; - } lang.lang().load(); lang.lang().save(); return success; diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java index 34a4e9f..611e368 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java @@ -24,6 +24,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Getter @@ -65,15 +66,31 @@ protected boolean initialize() { @Override protected boolean terminate() { + boolean success = true; + AtomicInteger failedSaves = new AtomicInteger(0); getCached().forEach(payload -> { getNetworked(payload).ifPresent(no -> { if (no.isThisMostRelevantServer()) { - save(payload); + if (!save(payload)) { + failedSaves.getAndIncrement(); + } } }); }); + if (failedSaves.get() > 0) { + errorService.capture(failedSaves + " objects failed to save during shutdown"); + success = false; + } + controllers.clear(); - return true; + if (!localStore.shutdown()) { + success = false; + } + if (!mongoStore.shutdown()) { + success = false; + } + + return success; } @Nonnull @@ -94,7 +111,7 @@ public boolean saveNoSync(@Nonnull X payload) { errorService.capture("Failed to save to MongoDB store for object " + payload.getIdentifier()); success = false; } - if (success) { + if (success && mode.equals(PayloadMode.NETWORK_NODE)) { Optional o = networkService.get(payload); if (o.isPresent()) { NetworkObject no = o.get(); @@ -152,13 +169,33 @@ public Set getAll() { @Override public int saveAll() { - int failures = 0; - for (X object : localStore.getLocalCache().values()) { - if (!save(object)) { - failures++; + AtomicInteger failures = new AtomicInteger(); + if (mode.equals(PayloadMode.NETWORK_NODE)) { + for (X object : localStore.getAll()) { + getNetworked(object).ifPresent(networkObject -> { + if (networkObject.isThisMostRelevantServer()) { + networkObject.markSaved(); + boolean fail = false; + if (!getNetworkService().save(networkObject)) { + fail = true; + } + if (!save(object)) { + fail = true; + } + if (fail) { + failures.getAndIncrement(); + } + } + }); + } + } else { + for (X object : localStore.getAll()) { + if (!save(object)) { + failures.getAndIncrement(); + } } } - return failures; + return failures.get(); } @Override diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index b65d5b6..fa3f5c3 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Getter @@ -89,8 +90,16 @@ protected boolean initialize() { @Override protected boolean terminate() { boolean success = true; + AtomicInteger failedSaves = new AtomicInteger(0); for (Player player : plugin.getServer().getOnlinePlayers()) { - getFromCache(player).ifPresent(this::save); + getFromCache(player).ifPresent(payload -> { + if (!save(payload)) { + failedSaves.getAndIncrement(); + } + }); + } + if (failedSaves.get() > 0) { + errorService.capture(failedSaves + " objects failed to save during shutdown"); } controllers.clear(); if (!localStore.shutdown()) { @@ -99,6 +108,8 @@ protected boolean terminate() { if (!mongoStore.shutdown()) { success = false; } + + return success; } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index 277213f..da0a0b3 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -34,6 +34,7 @@ public class PayloadProfileController implements Paylo private Player player = null; private boolean failure = false; private boolean loadedFromLocal = false; + private int timeoutAttempts = 0; PayloadProfileController(@Nonnull PayloadProfileCache cache, @Nonnull UUID uuid) { Preconditions.checkNotNull(cache); @@ -131,6 +132,7 @@ private Optional cacheStandalone() { } if (payload != null) { if (login) { + timeoutAttempts = 0; Optional oNP = cache.getNetworked(payload); NetworkProfile networkProfile = oNP.orElseGet(() -> cache.getNetworkService().create(payload)); @@ -213,13 +215,21 @@ private Optional cacheNetworkNode() { HandshakeHandler handshake = cache.getHandshakeService().publish(new ProfileHandshake(cache.getInjector(), cache, uuid, server.getName())); Optional o = handshake.waitForReply(cache.getSettings().getHandshakeTimeoutSeconds()); if (o.isPresent()) { + timeoutAttempts = 0; cache.getErrorService().debug("Handshake complete for " + uuid.toString() + ", loading from DB"); load(false); } else { // Timed out - denyJoin = true; - joinDenyReason = ChatColor.RED + "Timed out while loading your profile. Please try again."; - cache.getErrorService().debug("Handshake timed out for " + uuid.toString()); + timeoutAttempts++; + if (timeoutAttempts >= cache.getSettings().getHandshakeTimeOutAttemptsAllowJoin()) { + timeoutAttempts = 0; + load(false); + // They timed out past the max threshold specified, allow them to join / load from database + } else { + denyJoin = true; + joinDenyReason = ChatColor.RED + "Timed out while loading your profile. Please try again."; + cache.getErrorService().debug("Handshake timed out for " + uuid.toString()); + } } } else { // Target server isn't online, or there is no recent server @@ -253,6 +263,7 @@ private Optional cacheNetworkNode() { } } if (login) { + timeoutAttempts = 0; if (networkProfile == null) { networkProfile = cache.getNetworkService().create(payload); } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index 9f1b67f..ca5053e 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -119,13 +119,8 @@ public void onProfileQuit(final PlayerQuitEvent event) { // Not switching servers (no incoming handshake) -- we can assume they are actually // Logging out, and not switching servers + cache.save(profile); cache.controller(event.getPlayer().getUniqueId()).uncache(profile, false); - cache.save(profile); // Don't publish a sync since we're switching servers - // In network node mode, join is handled before quit when switching servers - // so we don't want to save on quit - // but we do want to remove their locally cached profile because the data will be outdated - // and we want to prevent accidental data rollbacks - cache.getLocalStore().remove(player.getUniqueId()); cache.removeController(player.getUniqueId()); cache.getErrorService().debug("Saving player " + player.getName() + " on logout (not switching servers)"); } else { @@ -133,9 +128,6 @@ public void onProfileQuit(final PlayerQuitEvent event) { cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); - - // Switching servers, don't save their data -- just remove - cache.getLocalStore().remove(player.getUniqueId()); // remove on quit to prevent accidental data rollbacks cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); } } else { From c475eba2a0f9379cf1e78ab0915d8dd1f9d57cb3 Mon Sep 17 00:00:00 2001 From: Jonah Date: Sun, 22 Dec 2019 18:16:48 -0700 Subject: [PATCH 06/20] Last for 3.0.0 --- pom.xml | 2 +- .../payload/base/handshake/Handshake.java | 26 ++++++++---- .../payload/base/network/NetworkPayload.java | 13 +++--- .../payload/command/commands/CmdProfile.java | 7 ++-- .../database/InternalPayloadDatabase.java | 6 +-- .../payload/mode/object/NetworkObject.java | 11 ++--- .../mode/object/PayloadObjectCache.java | 34 ++++++++------- .../mode/object/PayloadObjectController.java | 7 ++-- .../payload/mode/profile/NetworkProfile.java | 19 ++++----- .../mode/profile/PayloadProfileCache.java | 6 +++ .../profile/PayloadProfileController.java | 21 +++++----- .../mode/profile/ProfileHandshake.java | 6 ++- .../profile/listener/ProfileListener.java | 42 ++++++++----------- .../mode/profile/store/ProfileStoreMongo.java | 4 +- .../payload/server/PayloadServerService.java | 12 ++++-- 15 files changed, 116 insertions(+), 100 deletions(-) diff --git a/pom.xml b/pom.xml index e013df4..1b3cfa4 100644 --- a/pom.xml +++ b/pom.xml @@ -104,7 +104,7 @@ redis.clients jedis - 3.1.0 + 3.2.0 jar compile diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java b/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java index 8696243..5ee9b10 100644 --- a/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java +++ b/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java @@ -21,8 +21,9 @@ public abstract class Handshake { protected HandshakeService handshakeService; @Inject protected DatabaseService database; - protected HandshakeListener listener; + protected HandshakeListener listener = null; protected HandshakeHandler handler; + protected Jedis subscriber = null; @Inject public Handshake(Injector injector) { @@ -62,12 +63,19 @@ void executeHandler() { } public void listen() { - listener = new HandshakeListener(handshakeService, this); - try (Jedis subscriber = database.getJedisResource()) { - subscriber.subscribe(listener, channelPublish(), channelReply()); - } catch (Exception ex) { - database.getErrorService().capture(ex, "Error during listening for handshake " + this.getClass().getSimpleName()); - ex.printStackTrace(); + if (listener == null) { + listener = new HandshakeListener(handshakeService, this); + } + if (subscriber == null) { + subscriber = database.getJedisResource(); + } + if (!listener.isSubscribed()) { + try { + subscriber.subscribe(listener, channelPublish(), channelReply()); + } catch (Exception ex) { + database.getErrorService().capture(ex, "Error during listening for handshake " + this.getClass().getSimpleName()); + ex.printStackTrace(); + } } } @@ -77,6 +85,10 @@ public void stopListening() { listener.unsubscribe(); } } + if (subscriber != null) { + subscriber.close(); + subscriber = null; + } } public abstract Handshake create(); diff --git a/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java b/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java index 8785714..f70141a 100644 --- a/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java +++ b/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java @@ -10,14 +10,12 @@ import dev.morphia.annotations.Embedded; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Id; +import dev.morphia.annotations.Transient; import lombok.Getter; import lombok.Setter; import org.bson.types.ObjectId; import javax.annotation.Nonnull; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; @Getter @Setter @@ -27,13 +25,12 @@ public abstract class NetworkPayload { @Id private ObjectId id = new ObjectId(); // required for morphia mapping + @Transient protected transient final ServerService serverService; - protected ObjectId objectId; - protected Date lastCached = new Date(); - protected Date lastSaved = new Date(); + protected long lastCached = System.currentTimeMillis(); + protected long lastSaved = System.currentTimeMillis(); protected boolean loaded = false; @Embedded - protected Set loadedServers = new HashSet<>(); protected String mostRecentServer; @Inject @@ -43,7 +40,7 @@ public NetworkPayload(ServerService serverService) { public boolean isThisMostRelevantServer() { if (mostRecentServer != null) { - return mostRecentServer.equalsIgnoreCase(serverService.getThisServer().getName()) && loadedServers.contains(serverService.getThisServer().getName()) && loaded; + return mostRecentServer.equalsIgnoreCase(serverService.getThisServer().getName()) && loaded; } return false; } diff --git a/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java b/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java index 8362512..4b16109 100644 --- a/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java +++ b/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java @@ -8,7 +8,6 @@ import com.google.inject.Inject; import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.base.Cache; -import com.jonahseguin.payload.base.PayloadCache; import com.jonahseguin.payload.base.PayloadPermission; import com.jonahseguin.payload.command.CmdArgs; import com.jonahseguin.payload.command.PayloadCommand; @@ -64,9 +63,9 @@ public void execute(CmdArgs args) { args.msg("&7Online: &6{0}", (np.isOnline() ? "&aYes" : "&cNo")); args.msg("&7Loaded: &6{0}", (np.isLoaded() ? "&aYes" : "&cNo")); args.msg("&7Last Seen On: &6{0}", np.getLastSeenServer() != null ? np.getLastSeenServer() : "&cN/A"); - args.msg("&7Last Seen At: &6{0}", np.getLastSeen() != null ? np.getLastSeen().toString() : "&cN/A"); - args.msg("&7Last Saved: &6{0}", np.getLastSaved() != null ? np.getLastSaved().toString() : "&cN/A"); - args.msg("&7Last Cached: &6{0}", np.getLastCached() != null ? np.getLastCached().toString() : "&cN/A"); + args.msg("&7Last Seen At: &6{0}", np.getLastSeen() > 0 ? formatDateTime(np.getLastSeen()) : "&cN/A"); + args.msg("&7Last Saved: &6{0}", np.getLastSaved() > 0 ? formatDateTime(np.getLastSaved()) : "&cN/A"); + args.msg("&7Last Cached: &6{0}", np.getLastCached() > 0 ? formatDateTime(np.getLastCached()) : "&cN/A"); } } else { args.msg("&cPayload: A profile with username '{0}' does not exist in cache '{1}'.", playerName); diff --git a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java b/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java index 4dfd0d9..75a944d 100644 --- a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java +++ b/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java @@ -177,9 +177,9 @@ public boolean connectRedis() { if (this.jedisPool == null) { GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); - poolConfig.setMaxTotal(256); - poolConfig.setMaxIdle(32); - poolConfig.setMinIdle(2); + poolConfig.setMaxTotal(64); + poolConfig.setMaxIdle(16); + poolConfig.setMinIdle(8); if (payloadRedis.useURI()) { jedisPool = new JedisPool(poolConfig, URI.create(payloadRedis.getUri())); diff --git a/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java b/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java index d0f2188..d4a19b8 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java @@ -12,12 +12,11 @@ import dev.morphia.annotations.Entity; import javax.annotation.Nonnull; -import java.util.Date; @Entity public class NetworkObject extends NetworkPayload { - private String identifier = null; + private String identifier = ""; @Inject public NetworkObject(ServerService serverService) { @@ -26,19 +25,17 @@ public NetworkObject(ServerService serverService) { public void markLoaded() { loaded = true; - loadedServers.add(serverService.getThisServer().getName()); - lastCached = new Date(); + lastCached = System.currentTimeMillis(); mostRecentServer = serverService.getThisServer().getName(); } public void markUnloaded() { - loadedServers.remove(serverService.getThisServer().getName()); - loaded = loadedServers.size() > 0; + loaded = false; } public void markSaved() { mostRecentServer = serverService.getThisServer().getName(); - lastSaved = new Date(); + lastSaved = System.currentTimeMillis(); } @Override diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java index 611e368..675288e 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java @@ -20,7 +20,6 @@ import javax.annotation.Nonnull; import java.util.Collection; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -57,9 +56,9 @@ protected boolean initialize() { errorService.capture("Failed to start MongoDB store for cache " + name); } } - if (mode.equals(PayloadMode.NETWORK_NODE)) { + /*if (mode.equals(PayloadMode.NETWORK_NODE)) { handshakeService.subscribe(new ObjectHandshake(injector, this)); - } + }*/ database.getMorphia().map(NetworkObject.class); return success; } @@ -111,17 +110,18 @@ public boolean saveNoSync(@Nonnull X payload) { errorService.capture("Failed to save to MongoDB store for object " + payload.getIdentifier()); success = false; } - if (success && mode.equals(PayloadMode.NETWORK_NODE)) { - Optional o = networkService.get(payload); - if (o.isPresent()) { - NetworkObject no = o.get(); - no.markSaved(); - if (!networkService.save(no)) { - success = false; - errorService.capture("Failed to save Network Object for object " + payload.getIdentifier()); + /*if (success && mode.equals(PayloadMode.NETWORK_NODE)) { + runAsync(() -> { + Optional o = networkService.get(payload); + if (o.isPresent()) { + NetworkObject no = o.get(); + no.markSaved(); + if (!networkService.save(no)) { + errorService.capture("Failed to save Network Object for object " + payload.getIdentifier()); + } } - } - } + }); + }*/ return success; } @@ -171,7 +171,8 @@ public Set getAll() { public int saveAll() { AtomicInteger failures = new AtomicInteger(); if (mode.equals(PayloadMode.NETWORK_NODE)) { - for (X object : localStore.getAll()) { + /*for (X object : localStore.getAll()) { + save(object); getNetworked(object).ifPresent(networkObject -> { if (networkObject.isThisMostRelevantServer()) { networkObject.markSaved(); @@ -187,6 +188,11 @@ public int saveAll() { } } }); + }*/ + for (X object : localStore.getAll()) { + if (!save(object)) { + failures.getAndIncrement(); + } } } else { for (X object : localStore.getAll()) { diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java index 5069f63..529bf6a 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java @@ -7,8 +7,6 @@ import com.google.common.base.Preconditions; import com.jonahseguin.payload.PayloadMode; -import com.jonahseguin.payload.base.handshake.HandshakeHandler; -import com.jonahseguin.payload.base.sync.SyncMode; import com.jonahseguin.payload.base.type.PayloadController; import lombok.Getter; import lombok.Setter; @@ -48,7 +46,7 @@ private void load(boolean fromLocal) { @Override public Optional cache() { - if (cache.getSyncMode().equals(SyncMode.ALWAYS) && cache.getSettings().isEnableSync() && cache.isCached(identifier)) { + /*if (cache.getSyncMode().equals(SyncMode.ALWAYS) && cache.getSettings().isEnableSync() && cache.isCached(identifier)) { load(true); } else { if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { @@ -84,7 +82,8 @@ public Optional cache() { // Standalone mode load(true); } - } + }*/ + load(true); if (payload != null && !loadedFromLocal) { this.cache.cache(payload); diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java b/src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java index 7afe3b7..b92e155 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java @@ -14,7 +14,6 @@ import lombok.Setter; import javax.annotation.Nonnull; -import java.util.Date; import java.util.UUID; @Entity @@ -24,7 +23,7 @@ public class NetworkProfile extends NetworkPayload { protected String identifier; protected String lastSeenServer; - protected Date lastSeen = new Date(); + protected long lastSeen = 0L; protected boolean online = false; protected transient UUID uuidID = null; @@ -48,7 +47,7 @@ private void setUUID() { } public boolean isOnline() { - boolean shouldBeOnline = (System.currentTimeMillis() - lastSeen.getTime()) < (1000 * 60 * 60); + boolean shouldBeOnline = (System.currentTimeMillis() - lastSeen) < (1000 * 60 * 60); if (online && !shouldBeOnline) { online = false; } @@ -58,26 +57,24 @@ public boolean isOnline() { public void markLoaded(boolean online) { this.online = online; loaded = true; - loadedServers.add(serverService.getThisServer().getName()); - lastCached = new Date(); + lastCached = System.currentTimeMillis(); if (online) { mostRecentServer = serverService.getThisServer().getName(); lastSeenServer = serverService.getThisServer().getName(); - lastSeen = new Date(); + lastSeen = System.currentTimeMillis(); } } public void markUnloaded(boolean switchingServers) { online = switchingServers; - loadedServers.remove(serverService.getThisServer().getName()); - loaded = loadedServers.size() > 0 || switchingServers; - lastSeen = new Date(); + loaded = switchingServers; + lastSeen = System.currentTimeMillis(); } public void markSaved() { - lastSaved = new Date(); + lastSaved = System.currentTimeMillis(); if (isOnlineThisServer()) { - lastSeen = new Date(); + lastSeen = System.currentTimeMillis(); } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index fa3f5c3..2d81315 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -188,6 +188,12 @@ public Optional get(@Nonnull String username) { if (player != null && player.isOnline()) { return get(player); } + UUID uuid = uuidService.get(username).orElse(null); + if (uuid != null) { + if (isCached(uuid)) { + return getFromCache(uuid); + } + } return mongoStore.getByUsername(username); } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index da0a0b3..72ff306 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -27,13 +27,12 @@ public class PayloadProfileController implements Paylo private final UUID uuid; private String username = null; private String loginIp = null; - private boolean login = true; // whether this controller is being used during a login operation + private boolean login = false; // whether this controller is being used during a login operation private boolean denyJoin = false; private String joinDenyReason = ChatColor.RED + "A caching error occurred. Please try again."; private X payload = null; private Player player = null; private boolean failure = false; - private boolean loadedFromLocal = false; private int timeoutAttempts = 0; PayloadProfileController(@Nonnull PayloadProfileCache cache, @Nonnull UUID uuid) { @@ -164,7 +163,6 @@ private void load(boolean local) { Optional o = cache.getLocalStore().get(uuid); if (o.isPresent()) { payload = o.get(); - loadedFromLocal = true; return; } } @@ -199,6 +197,9 @@ private Optional cacheNetworkNode() { // Just getting them from the cache when they are online, skip all the other shit and just return this. // For performance :) if (payload != null) { + if (!cache.isCached(uuid)) { + cache.cache(payload); + } return Optional.of(payload); } } @@ -250,6 +251,9 @@ private Optional cacheNetworkNode() { } if (payload != null) { + if (login || cache.getSettings().isAlwaysCacheOnLoadNetworkNode()) { + cache.cache(payload); + } if (username != null && !payload.getUsername().equalsIgnoreCase(username)) { cache.getErrorService().debug("Updated username: " + payload.getUsername() + " to " + username); payload.setUsername(username); @@ -257,11 +261,6 @@ private Optional cacheNetworkNode() { cache.getErrorService().capture("Error saving Payload during caching after username update: " + payload.getUsername()); } } - if (!loadedFromLocal) { - if (login || cache.getSettings().isAlwaysCacheOnLoadNetworkNode()) { - cache.cache(payload); - } - } if (login) { timeoutAttempts = 0; if (networkProfile == null) { @@ -278,11 +277,13 @@ private Optional cacheNetworkNode() { public void initializeOnJoin(Player player) { this.player = player; + if (payload == null) { + payload = cache.getFromCache(player).orElse(null); + } if (payload != null) { cache.getErrorService().debug("called initializeOnJoin() in controller for " + player.getName()); payload.initializePlayer(player); - } - else { + } else { cache.getErrorService().debug("failed to call initializeOnJoin() for " + player.getName() + " (payload is null in controller)"); } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java index f2257c2..56b35e1 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java @@ -79,6 +79,9 @@ public void receive() { if (!cache.save(profile)) { cache.getErrorService().capture("Failed to save during handshake for " + profile.getName()); } + } else { + // We don't have them but they tried to handshake from here + cache.getErrorService().debug("Skipping saving for Profile Handshake (not cached), replying: " + uuid.toString()); } } @@ -89,7 +92,6 @@ public boolean shouldReply() { @Override public boolean shouldAccept() { - Optional o = cache.getLocalStore().get(uuid); - return targetServer.equalsIgnoreCase(cache.getDatabase().getServerService().getThisServer().getName()) && o.isPresent() && o.get().isOnline(); + return targetServer.equalsIgnoreCase(cache.getDatabase().getServerService().getThisServer().getName()); } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index ca5053e..6607e4b 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -20,7 +20,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.Date; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -62,7 +61,7 @@ public void onProfileCachingInit(PlayerJoinEvent event) { api.getSortedCachesByDepends().forEach(c -> { if (c instanceof PayloadProfileCache) { PayloadProfileCache cache = (PayloadProfileCache) c; - PayloadProfileController controller = cache.getController(player.getUniqueId()); + PayloadProfileController controller = cache.controller(player.getUniqueId()); if (controller != null) { cache.getErrorService().debug("Initializing player " + player.getName() + " for cache " + cache.getName()); controller.initializeOnJoin(player); @@ -94,7 +93,7 @@ public void onProfileQuit(final PlayerQuitEvent event) { Optional oNP = cache.getNetworked(profile); oNP.ifPresent(networkProfile -> { networkProfile.setOnline(false); - networkProfile.setLastSeen(new Date()); + networkProfile.setLastSeen(System.currentTimeMillis()); cache.runAsync(() -> cache.getNetworkService().save(networkProfile)); }); @@ -110,29 +109,22 @@ public void onProfileQuit(final PlayerQuitEvent event) { if (o.isPresent()) { PayloadProfile profile = o.get(); profile.uninitializePlayer(); - Optional onp = cache.getNetworked(profile); - if (onp.isPresent()) { - NetworkProfile networkProfile = onp.get(); - if (!profile.hasValidHandshake()) { - PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); - cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); - - // Not switching servers (no incoming handshake) -- we can assume they are actually - // Logging out, and not switching servers - cache.save(profile); - cache.controller(event.getPlayer().getUniqueId()).uncache(profile, false); - cache.removeController(player.getUniqueId()); - cache.getErrorService().debug("Saving player " + player.getName() + " on logout (not switching servers)"); - } else { - PayloadProfileSwitchServersEvent payloadEvent = new PayloadProfileSwitchServersEvent(profile); - cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); - - cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); - cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); - } + if (!profile.hasValidHandshake()) { + PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); + cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + + // Not switching servers (no incoming handshake) -- we can assume they are actually + // Logging out, and not switching servers + cache.save(profile); + cache.controller(event.getPlayer().getUniqueId()).uncache(profile, false); + cache.removeController(player.getUniqueId()); + cache.getErrorService().debug("Saving player " + player.getName() + " on logout (not switching servers)"); } else { - // no network profile? - cache.getErrorService().debug("No network profile during logout for Payload" + player.getName()); + PayloadProfileSwitchServersEvent payloadEvent = new PayloadProfileSwitchServersEvent(profile); + cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + + cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); + cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); } } else { // This shouldn't happen diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/store/ProfileStoreMongo.java b/src/main/java/com/jonahseguin/payload/mode/profile/store/ProfileStoreMongo.java index a1db790..883da88 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/store/ProfileStoreMongo.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/store/ProfileStoreMongo.java @@ -31,7 +31,9 @@ public Optional get(@Nonnull UUID key) { try { Query q = getQuery(key); Stream stream = q.find().toList().stream(); - return stream.findFirst(); + Optional o = stream.findFirst(); + o.ifPresent(x -> x.setLoadingSource(layerName())); + return o; } catch (MongoException ex) { getCache().getErrorService().capture(ex, "MongoDB error getting Profile from MongoDB Layer: " + key.toString()); return Optional.empty(); diff --git a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java index 28b2daa..6f28f6d 100644 --- a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java +++ b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java @@ -56,8 +56,15 @@ public boolean start() { try { this.publisher = new ServerPublisher(this); + payloadPlugin.getServer().getScheduler().runTaskAsynchronously(payloadPlugin, () -> { + this.jedisSubscriber = database.getJedisResource(); + this.subscriber = new ServerSubscriber(this); + this.jedisSubscriber.subscribe(this.subscriber, + "server-join", "server-ping", "server-quit", "server-update-name"); + }); + this.executorService.submit(() -> { - this.jedisSubscriber = this.database.getJedisResource(); + this.jedisSubscriber = database.getJedisResource(); this.subscriber = new ServerSubscriber(this); this.jedisSubscriber.subscribe(this.subscriber, "server-join", "server-ping", "server-quit", "server-update-name"); @@ -146,7 +153,7 @@ public boolean shutdown() { if (this.pingTask != null) { this.pingTask.cancel(); } - + this.shutdownExecutor(); if (this.subscriber != null) { if (this.subscriber.isSubscribed()) { this.subscriber.unsubscribe(); @@ -155,7 +162,6 @@ public boolean shutdown() { } this.publisher.publishQuit(); // Sync. - this.shutdownExecutor(); this.publisher = null; if (this.jedisSubscriber != null) { From 70f91b75336f4cb4381ddfa0deea23a3c664379a Mon Sep 17 00:00:00 2001 From: Jonah Date: Mon, 23 Dec 2019 02:06:15 -0700 Subject: [PATCH 07/20] Start of 3.1.0 --- pom.xml | 32 ++-- .../com/jonahseguin/payload/PayloadAPI.java | 29 +-- .../jonahseguin/payload/PayloadModule.java | 5 +- .../jonahseguin/payload/PayloadPlugin.java | 39 +--- .../com/jonahseguin/payload/base/Cache.java | 41 +---- .../payload/base/PayloadCache.java | 169 ++---------------- .../payload/base/error/CacheErrorService.java | 97 ++-------- .../payload/base/error/ErrorService.java | 22 +-- .../payload/base/handshake/Handshake.java | 96 ---------- .../base/handshake/HandshakeContainer.java | 27 --- .../payload/base/handshake/HandshakeData.java | 32 ---- .../base/handshake/HandshakeHandler.java | 50 ------ .../base/handshake/HandshakeListener.java | 37 ---- .../base/handshake/HandshakeService.java | 28 --- .../handshake/PayloadHandshakeService.java | 142 --------------- .../payload/base/lang/LangService.java | 28 --- .../jonahseguin/payload/base/lang/PLang.java | 37 ++++ .../payload/base/lang/PLangService.java | 54 ++++++ .../payload/base/lang/PSettings.java | 80 +++++++++ .../payload/base/lang/PayloadLangService.java | 57 ------ .../payload/base/listener/LockListener.java | 63 ------- .../payload/base/network/NetworkPayload.java | 52 ------ .../payload/base/network/NetworkService.java | 29 --- .../base/network/RedisNetworkService.java | 136 -------------- .../base/service/PayloadObjectService.java | 1 - .../base/service/PayloadProfileService.java | 8 +- .../payload/base/service/PayloadService.java | 9 +- .../payload/base/settings/CacheSettings.java | 6 +- .../payload/base/sync/CacheSyncService.java | 78 -------- .../payload/base/sync/SyncHandshake.java | 92 ---------- .../payload/base/sync/SyncHandshakeMode.java | 13 -- .../payload/base/sync/SyncMode.java | 13 -- .../payload/base/sync/SyncService.java | 24 --- .../base/task/PayloadAutoSaveTask.java | 7 +- .../payload/command/PCommandHandler.java | 38 ++-- .../payload/command/commands/CmdProfile.java | 3 +- .../payload/database/DatabaseDependent.java | 41 +---- .../database/DatabaseErrorService.java | 152 ---------------- .../payload/database/DatabaseModule.java | 3 + .../payload/database/DatabaseService.java | 11 +- .../payload/database/PayloadDatabase.java | 13 +- .../database/error/DatabaseErrorService.java | 63 +++++++ .../InternalPayloadDatabase.java | 53 +++--- .../PayloadDatabaseService.java | 33 ++-- .../database/mongo/PayloadMongoMonitor.java | 23 +-- .../payload/database/redis/PayloadRedis.java | 21 +++ .../database/redis/PayloadRedisMonitor.java | 33 ++-- .../payload/mode/object/NetworkObject.java | 51 ------ .../payload/mode/object/ObjectCache.java | 2 +- .../payload/mode/object/ObjectHandshake.java | 84 --------- .../mode/object/PayloadObjectCache.java | 84 +-------- .../mode/object/PayloadObjectController.java | 8 - .../object/settings/ObjectCacheSettings.java | 1 - .../payload/mode/profile/PayloadProfile.java | 2 +- .../mode/profile/PayloadProfileCache.java | 119 +++++------- .../profile/PayloadProfileController.java | 37 +++- .../payload/mode/profile/ProfileCache.java | 17 +- .../mode/profile/ProfileHandshake.java | 97 ---------- .../handshake/ProfileHandshakePacket.java | 53 ++++++ .../handshake/ProfileHandshakeService.java | 138 ++++++++++++++ .../profile/listener/ProfileListener.java | 6 +- .../profile/{ => network}/NetworkProfile.java | 25 +-- .../mode/profile/network/NetworkService.java | 29 +++ .../profile/network/RedisNetworkService.java | 164 +++++++++++++++++ .../payload/server/PayloadServerService.java | 96 +++++----- .../payload/server/ServerEvent.java | 8 +- .../payload/server/ServerPublisher.java | 46 +++-- .../payload/server/ServerSubscriber.java | 43 ----- 68 files changed, 1016 insertions(+), 2214 deletions(-) delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/HandshakeContainer.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/HandshakeHandler.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/lang/LangService.java create mode 100644 src/main/java/com/jonahseguin/payload/base/lang/PLang.java create mode 100644 src/main/java/com/jonahseguin/payload/base/lang/PLangService.java create mode 100644 src/main/java/com/jonahseguin/payload/base/lang/PSettings.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/lang/PayloadLangService.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/listener/LockListener.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/network/NetworkService.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/network/RedisNetworkService.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/sync/SyncHandshakeMode.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/sync/SyncMode.java delete mode 100644 src/main/java/com/jonahseguin/payload/base/sync/SyncService.java delete mode 100644 src/main/java/com/jonahseguin/payload/database/DatabaseErrorService.java create mode 100644 src/main/java/com/jonahseguin/payload/database/error/DatabaseErrorService.java rename src/main/java/com/jonahseguin/payload/database/{ => internal}/InternalPayloadDatabase.java (90%) rename src/main/java/com/jonahseguin/payload/database/{ => internal}/PayloadDatabaseService.java (83%) delete mode 100644 src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java delete mode 100644 src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java delete mode 100644 src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java create mode 100644 src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java create mode 100644 src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java rename src/main/java/com/jonahseguin/payload/mode/profile/{ => network}/NetworkProfile.java (81%) create mode 100644 src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkService.java create mode 100644 src/main/java/com/jonahseguin/payload/mode/profile/network/RedisNetworkService.java delete mode 100644 src/main/java/com/jonahseguin/payload/server/ServerSubscriber.java diff --git a/pom.xml b/pom.xml index 1b3cfa4..1c92b48 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.jonahseguin Payload - 3.0.0 + 3.1.0 UTF-8 @@ -60,6 +60,19 @@ + + org.projectlombok + lombok-maven-plugin + 1.18.10.0 + + + generate-sources + + delombok + + + + @@ -80,7 +93,7 @@ org.projectlombok lombok - 1.16.10 + 1.18.10 provided @@ -102,11 +115,9 @@ compile - redis.clients - jedis - 3.2.0 - jar - compile + io.lettuce + lettuce-core + 5.2.1.RELEASE com.google.guava @@ -126,13 +137,6 @@ 4.2.2 compile - - com.jonahseguin - lang - 1.0.0 - compile - - diff --git a/src/main/java/com/jonahseguin/payload/PayloadAPI.java b/src/main/java/com/jonahseguin/payload/PayloadAPI.java index 7e449d1..a4c732e 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadAPI.java +++ b/src/main/java/com/jonahseguin/payload/PayloadAPI.java @@ -8,8 +8,6 @@ import com.google.common.base.Preconditions; import com.jonahseguin.payload.base.Cache; import com.jonahseguin.payload.base.PayloadCache; -import com.jonahseguin.payload.base.handshake.HandshakeService; -import com.jonahseguin.payload.base.network.NetworkPayload; import com.jonahseguin.payload.base.type.Payload; import com.jonahseguin.payload.database.DatabaseModule; import com.jonahseguin.payload.database.PayloadDatabase; @@ -31,7 +29,6 @@ public class PayloadAPI { private final ConcurrentMap caches = new ConcurrentHashMap<>(); private final ConcurrentMap databases = new ConcurrentHashMap<>(); private final ConcurrentMap serverServices = new ConcurrentHashMap<>(); - private final ConcurrentMap handshakeServices = new ConcurrentHashMap<>(); private final Set requested = new HashSet<>(); private List _sortedCaches = null; @@ -105,33 +102,17 @@ public void registerServerService(ServerService serverService) { } } - public boolean isHandshakeServiceRegistered(String name) { - return this.handshakeServices.containsKey(name.toLowerCase()); - } - - public HandshakeService getHandshakeService(String name) { - return this.handshakeServices.get(name.toLowerCase()); - } - - public void registerHandshakeService(HandshakeService handshakeService) { - if (!isHandshakeServiceRegistered(handshakeService.getName())) { - this.handshakeServices.put(handshakeService.getName(), handshakeService); - } else { - throw new IllegalArgumentException("A Payload Server Service (database) with the name '" + handshakeService.getName() + "' has already been registered. Choose a different name."); - } - } - - /** * Get a cache by name + * * @param name Name of the cache - * @param Key type (i.e String for uuid) - * @param Value type (object to cache; i.e Profile) + * @param Key type (i.e String for uuid) + * @param Value type (object to cache; i.e Profile) * @return The Cache */ @SuppressWarnings("unchecked") // bad, oops - public , N extends NetworkPayload> Cache getCache(String name) { - return (Cache) this.caches.get(convertCacheName(name)); + public > Cache getCache(String name) { + return (Cache) this.caches.get(convertCacheName(name)); } public Cache getCacheRaw(String name) { diff --git a/src/main/java/com/jonahseguin/payload/PayloadModule.java b/src/main/java/com/jonahseguin/payload/PayloadModule.java index 69b7375..4e32b7b 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadModule.java +++ b/src/main/java/com/jonahseguin/payload/PayloadModule.java @@ -10,8 +10,7 @@ import com.google.inject.Singleton; import com.jonahseguin.payload.base.CacheService; import com.jonahseguin.payload.base.DatabaseCacheService; -import com.jonahseguin.payload.base.lang.LangService; -import com.jonahseguin.payload.base.lang.PayloadLangService; +import com.jonahseguin.payload.base.lang.PLangService; import com.jonahseguin.payload.base.lifecycle.LifecycleService; import com.jonahseguin.payload.base.lifecycle.PluginLifecycleService; import com.jonahseguin.payload.base.uuid.UUIDService; @@ -40,7 +39,7 @@ protected void configure() { bind(PayloadAPI.class).toInstance(payloadPlugin.getApi()); bind(PayloadPlugin.class).toInstance(payloadPlugin); bind(PayloadLocal.class).toInstance(payloadPlugin.getLocal()); - bind(LangService.class).to(PayloadLangService.class); + bind(PLangService.class).toInstance(payloadPlugin.getLang()); bind(Plugin.class).toInstance(plugin); bind(JavaPlugin.class).toInstance(plugin); diff --git a/src/main/java/com/jonahseguin/payload/PayloadPlugin.java b/src/main/java/com/jonahseguin/payload/PayloadPlugin.java index 749a9a8..fafff9c 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadPlugin.java +++ b/src/main/java/com/jonahseguin/payload/PayloadPlugin.java @@ -10,9 +10,7 @@ import com.google.inject.Singleton; import com.google.inject.Stage; import com.jonahseguin.payload.base.PayloadPermission; -import com.jonahseguin.payload.base.lang.LangService; -import com.jonahseguin.payload.base.lang.PayloadLangService; -import com.jonahseguin.payload.base.listener.LockListener; +import com.jonahseguin.payload.base.lang.PLangService; import com.jonahseguin.payload.command.PCommandHandler; import com.jonahseguin.payload.mode.profile.listener.ProfileListener; import lombok.Getter; @@ -37,10 +35,9 @@ public class PayloadPlugin extends JavaPlugin { private final PayloadAPI api = new PayloadAPI(this); private static PayloadPlugin plugin = null; private Injector injector = null; - private boolean locked = false; private final PayloadLocal local = new PayloadLocal(this); private PCommandHandler commandHandler; - private LangService lang; + private PLangService lang; /** * Format a string with arguments @@ -91,35 +88,17 @@ public void onEnable() { this.getLogger().info("This is the first startup for Payload on this server instance. Files created."); } + this.lang = new PLangService(this); + injector = Guice.createInjector(Stage.PRODUCTION, PayloadAPI.install(this, "PayloadDatabase")); - lang = new PayloadLangService(this); - lang.lang().load(); - lang.lang().save(); commandHandler = new PCommandHandler(this, lang, injector); - this.getServer().getPluginManager().registerEvents(injector.getInstance(LockListener.class), this); this.getServer().getPluginManager().registerEvents(injector.getInstance(ProfileListener.class), this); this.getCommand("payload").setExecutor(this.commandHandler); this.getLogger().info(PayloadPlugin.format("Payload v{0} by Jonah Seguin enabled.", getDescription().getVersion())); } - /** - * Whether to globally lock the server from players joining that don't have the bypass permission - * @return Locked - */ - public boolean isLocked() { - return locked; - } - - /** - * Change the status of server join lock - * @param locked is it locked? - */ - public void setLocked(boolean locked) { - this.locked = locked; - } - /** * Get the Local ID handler for this Payload instance on a specific server instance * The {@link PayloadLocal} instance can be used to get the unique ID for this server. @@ -139,8 +118,8 @@ public boolean isDebug() { @Override public void onDisable() { - lang.lang().load(); - lang.lang().save(); + lang.load(); + lang.save(); this.getLogger().info(PayloadPlugin.format("Payload v{0} by Jonah Seguin disabled.", getDescription().getVersion())); plugin = null; } @@ -168,12 +147,6 @@ public void setDebug(boolean debug) { } } - public void alert(PayloadPermission permission, String module, String key, Object... args) { - String l = lang.get(module, key, args); - getLogger().info(l); - getServer().getOnlinePlayers().stream().filter(p -> p.hasPermission(permission.getPermission())).forEach(p -> p.sendMessage(l)); - } - public void alert(PayloadPermission permission, String msg) { getLogger().info(msg); getServer().getOnlinePlayers().stream().filter(p -> p.hasPermission(permission.getPermission())).forEach(p -> p.sendMessage(msg)); diff --git a/src/main/java/com/jonahseguin/payload/base/Cache.java b/src/main/java/com/jonahseguin/payload/base/Cache.java index 73a5268..58a6179 100644 --- a/src/main/java/com/jonahseguin/payload/base/Cache.java +++ b/src/main/java/com/jonahseguin/payload/base/Cache.java @@ -9,14 +9,9 @@ import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.error.ErrorService; -import com.jonahseguin.payload.base.handshake.HandshakeService; -import com.jonahseguin.payload.base.lang.LangService; -import com.jonahseguin.payload.base.network.NetworkPayload; -import com.jonahseguin.payload.base.network.NetworkService; +import com.jonahseguin.payload.base.lang.PLangService; import com.jonahseguin.payload.base.settings.CacheSettings; import com.jonahseguin.payload.base.store.PayloadStore; -import com.jonahseguin.payload.base.sync.SyncMode; -import com.jonahseguin.payload.base.sync.SyncService; import com.jonahseguin.payload.base.type.Payload; import com.jonahseguin.payload.base.type.PayloadController; import com.jonahseguin.payload.base.type.PayloadInstantiator; @@ -27,28 +22,18 @@ import javax.annotation.Nonnull; import java.util.Collection; import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -public interface Cache, N extends NetworkPayload> extends Service, DatabaseDependent { - - Optional getNetworked(@Nonnull K key); - - Optional getNetworked(@Nonnull X payload); +public interface Cache> extends Service, DatabaseDependent { Optional get(@Nonnull K key); - Future> getAsync(@Nonnull K key); - Optional getFromCache(@Nonnull K key); Optional getFromDatabase(@Nonnull K key); boolean save(@Nonnull X payload); - Future saveAsync(@Nonnull X payload); - - boolean saveNoSync(@Nonnull X payload); + void saveAsync(@Nonnull X payload); void cache(@Nonnull X payload); @@ -74,12 +59,6 @@ public interface Cache, N extends NetworkPayload> ext @Nonnull Collection getCached(); - @Nonnull - NetworkService getNetworkService(); - - @Nonnull - SyncService getSyncService(); - @Nonnull ErrorService getErrorService(); @@ -126,10 +105,7 @@ public interface Cache, N extends NetworkPayload> ext void runAsync(@Nonnull Runnable runnable); @Nonnull - Future runAsync(@Nonnull Callable callable); - - @Nonnull - LangService getLang(); + PLangService getLang(); boolean isDebug(); @@ -137,11 +113,6 @@ public interface Cache, N extends NetworkPayload> ext void alert(@Nonnull PayloadPermission required, @Nonnull String msg); - @Nonnull - SyncMode getSyncMode(); - - void setSyncMode(@Nonnull SyncMode mode); - void updatePayloadID(); int cachedObjectCount(); @@ -153,10 +124,6 @@ public interface Cache, N extends NetworkPayload> ext X create(); - N createNetworked(); - - HandshakeService getHandshakeService(); - Injector getInjector(); } diff --git a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java index cd229ab..09f249c 100644 --- a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java +++ b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java @@ -14,14 +14,7 @@ import com.jonahseguin.payload.PayloadPlugin; import com.jonahseguin.payload.base.error.CacheErrorService; import com.jonahseguin.payload.base.error.ErrorService; -import com.jonahseguin.payload.base.handshake.HandshakeService; -import com.jonahseguin.payload.base.lang.LangService; -import com.jonahseguin.payload.base.network.NetworkPayload; -import com.jonahseguin.payload.base.network.NetworkService; -import com.jonahseguin.payload.base.network.RedisNetworkService; -import com.jonahseguin.payload.base.sync.CacheSyncService; -import com.jonahseguin.payload.base.sync.SyncMode; -import com.jonahseguin.payload.base.sync.SyncService; +import com.jonahseguin.payload.base.lang.PLangService; import com.jonahseguin.payload.base.task.PayloadAutoSaveTask; import com.jonahseguin.payload.base.type.Payload; import com.jonahseguin.payload.base.type.PayloadInstantiator; @@ -34,11 +27,10 @@ import org.bukkit.plugin.Plugin; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.HashSet; import java.util.Optional; import java.util.Set; -import java.util.concurrent.*; +import java.util.concurrent.Executors; /** * The abstract backbone of all Payload cache systems. @@ -46,45 +38,36 @@ */ @Getter @Singleton -public abstract class PayloadCache, N extends NetworkPayload> implements Comparable, Cache { +public abstract class PayloadCache> implements Comparable, Cache { - protected final ExecutorService pool = Executors.newCachedThreadPool(); - protected final PayloadAutoSaveTask autoSaveTask = new PayloadAutoSaveTask<>(this); + protected final PayloadAutoSaveTask autoSaveTask = new PayloadAutoSaveTask<>(this); protected final Set dependingCaches = new HashSet<>(); protected final Class keyClass; protected final Class payloadClass; - protected final Class networkClass; protected final String name; protected final Injector injector; @Inject protected Plugin plugin; @Inject protected PayloadPlugin payloadPlugin; @Inject protected PayloadAPI api; @Inject protected DatabaseService database; - @Inject protected LangService lang; - @Inject protected HandshakeService handshakeService; + @Inject protected PLangService lang; @Inject protected ServerService serverService; protected ErrorService errorService; - protected SyncService sync; - protected NetworkService networkService; protected PayloadInstantiator instantiator; - protected SyncMode syncMode = SyncMode.IF_CACHED; protected boolean debug = true; protected PayloadMode mode = PayloadMode.STANDALONE; protected boolean running = false; - public PayloadCache(Injector injector, PayloadInstantiator instantiator, String name, Class key, Class payload, Class network) { + public PayloadCache(Injector injector, PayloadInstantiator instantiator, String name, Class key, Class payload) { this.injector = injector; this.instantiator = instantiator; this.name = name; this.keyClass = key; this.payloadClass = payload; - this.networkClass = network; } protected void setupModule() { - this.sync = new CacheSyncService<>(this, handshakeService); - this.networkService = new RedisNetworkService<>(this, networkClass, database); - this.errorService = new CacheErrorService(this, lang); + this.errorService = new CacheErrorService(this); } protected void injectMe() { @@ -117,25 +100,7 @@ public final boolean start() { success = false; errorService.capture("Failed to initialize internally for cache " + name); } - if (!handshakeService.start()) { - success = false; - errorService.capture("Failed to start Handshake Service for cache " + name); - } - if (getMode().equals(PayloadMode.NETWORK_NODE)) { - if (!networkService.start()) { - success = false; - errorService.capture("Failed to start Network Service for cache " + name); - } - } autoSaveTask.start(); - if (getSettings().isEnableSync()) { - if (!sync.start()) { - success = false; - errorService.capture("Failed to start Sync Service for cache " + name); - } - } - lang.lang().load(); - lang.lang().save(); running = true; return success; } @@ -154,39 +119,10 @@ public final boolean shutdown() { } autoSaveTask.stop(); - if (!handshakeService.shutdown()) { - success = false; - } - if (getMode().equals(PayloadMode.NETWORK_NODE)) { - if (!networkService.shutdown()) { - success = false; - } - } - if (getSettings().isEnableSync()) { - this.sync.shutdown(); - } - shutdownPool(); running = false; - lang.lang().load(); - lang.lang().save(); return success; } - /** - * Internal method to safely shutdown the internal cache thread pool. - * This allows time for processes to finish executing before continuing. - */ - private void shutdownPool() { - try { - pool.shutdown(); - pool.awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException ex) { - errorService.capture(ex, "Interrupted during shutdown of cache's thread pool"); - } finally { - pool.shutdownNow(); - } - } - /** * Starts up & initializes the cache. * Prepares everything for a fresh startup, ensures database connections, etc. @@ -226,12 +162,6 @@ public void setDebug(boolean debug) { this.debug = debug; } - @Override - public void setSyncMode(@Nonnull SyncMode mode) { - Preconditions.checkNotNull(mode); - this.syncMode = mode; - } - /** * Internal method used by payload to provide a server-specific name for this cache, if server-specific caching is enabled. * This is primarily used by Redis layers for naming the redis key. @@ -301,30 +231,12 @@ protected final void updatePayloadFromNewer(@Nonnull X payload, @Nonnull X updat }); } - @Override - public Optional getNetworked(@Nonnull K key) { - Preconditions.checkNotNull(key); - return networkService.get(key); - } - - @Override - public Optional getNetworked(@Nonnull X payload) { - Preconditions.checkNotNull(payload); - return networkService.get(payload); - } - @Override public Optional get(@Nonnull K key) { Preconditions.checkNotNull(key); return controller(key).cache(); } - @Override - public Future> getAsync(@Nonnull K key) { - Preconditions.checkNotNull(key); - return runAsync(() -> get(key)); - } - @Override public Optional getFromCache(@Nonnull K key) { Preconditions.checkNotNull(key); @@ -340,20 +252,18 @@ public Optional getFromDatabase(@Nonnull K key) { @Override public boolean save(@Nonnull X payload) { Preconditions.checkNotNull(payload); - if (saveNoSync(payload)) { - if (getSettings().isEnableSync()) { - sync.update(payload.getIdentifier()); - } - return true; + cache(payload); + boolean mongo = getDatabaseStore().save(payload); + if (!mongo) { + errorService.capture("Failed to save payload " + keyToString(payload.getIdentifier())); } - errorService.capture("Failed to save payload " + keyToString(payload.getIdentifier()) + " (during saveNoSync())"); - return false; + return mongo; } @Override - public Future saveAsync(@Nonnull X payload) { + public void saveAsync(@Nonnull X payload) { Preconditions.checkNotNull(payload); - return runAsync(() -> save(payload)); + runAsync(() -> save(payload)); } @Override @@ -401,18 +311,18 @@ public boolean isCached(@Nonnull K key) { @Override public void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback> callback) { - Preconditions.checkState(getSettings().isEnableSync(), "Cannot prepare update when sync is disabled!"); Preconditions.checkNotNull(payload); Preconditions.checkNotNull(callback); - sync.prepareUpdate(payload, callback); + //sync.prepareUpdate(payload, callback); + callback.callback(Optional.of(payload)); // TODO } @Override public void prepareUpdateAsync(@Nonnull X payload, @Nonnull PayloadCallback> callback) { - Preconditions.checkState(getSettings().isEnableSync(), "Cannot prepare update when sync is disabled!"); Preconditions.checkNotNull(payload); Preconditions.checkNotNull(callback); - runAsync(() -> sync.prepareUpdate(payload, callback)); + //runAsync(() -> sync.prepareUpdate(payload, callback)); + callback.callback(Optional.of(payload)); // TODO } @Override @@ -426,32 +336,6 @@ public void cacheAll() { getDatabaseStore().getAll().forEach(this::cache); } - @Nonnull - @Override - public SyncService getSyncService() { - return sync; - } - - - /** - * Utility method to send a message to online players with a certain permission - * @param required The required permission - * @param module The language module - * @param key The language definition - * @param args The arguments for the language definition - */ - public void alert(@Nonnull PayloadPermission required, @Nonnull String module, @Nonnull String key, @Nullable Object... args) { - Preconditions.checkNotNull(required); - Preconditions.checkNotNull(module); - Preconditions.checkNotNull(key); - plugin.getLogger().info(lang.module(module).format(key, args)); - for (Player pl : plugin.getServer().getOnlinePlayers()) { - if (required.has(pl)) { - pl.sendMessage(lang.module(module).format(key, args)); - } - } - } - /** * Utility method to send a message to online players with a certain permission * @param required The required permission @@ -484,22 +368,7 @@ public X create() { @Override public void runAsync(@Nonnull Runnable runnable) { Preconditions.checkNotNull(runnable); - pool.submit(runnable); - } - - /** - * Simple utility method to run a task asynchronously in a separate thread provided by the cache's local cached thread executor pool. - * This is recommended over using the Bukkit scheduler when performing operations relative to the cache, as it will ensure operations - * are completed BEFORE cache shutdown, plus the cached thread nature yields a slight performance improvement. - * @see Executors#newCachedThreadPool() - * @param callable The task to run - * @return {@link Future} a future with the callable's parameter after execution has completed. - */ - @Nonnull - @Override - public Future runAsync(@Nonnull Callable callable) { - Preconditions.checkNotNull(callable); - return pool.submit(callable); + plugin.getServer().getScheduler().runTaskAsynchronously(plugin, runnable); } /** diff --git a/src/main/java/com/jonahseguin/payload/base/error/CacheErrorService.java b/src/main/java/com/jonahseguin/payload/base/error/CacheErrorService.java index 12a13af..130cd6e 100644 --- a/src/main/java/com/jonahseguin/payload/base/error/CacheErrorService.java +++ b/src/main/java/com/jonahseguin/payload/base/error/CacheErrorService.java @@ -5,116 +5,51 @@ package com.jonahseguin.payload.base.error; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; import com.jonahseguin.payload.base.Cache; -import com.jonahseguin.payload.base.lang.LangService; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -public class CacheErrorService implements ErrorService, LangModule { +public class CacheErrorService implements ErrorService { protected final Cache cache; - protected final LangService lang; - public CacheErrorService(Cache cache, LangService lang) { + public CacheErrorService(Cache cache) { this.cache = cache; - this.lang = lang; - lang.register(this); } - @Override - public void define(LangDefinitions l) { - l.define("error-generic", "&7[Error][{0}] {1}"); - l.define("error-specific", "&7[Error][{0}] {1} - {2}"); - l.define("debug", "&7[Debug][{0}] {1}"); - } - - @Override - public String langModule() { - return "error"; - } - - @Override - public String capture(@Nonnull Throwable throwable) { - String s = lang.module(this).format("error-generic", cache.getName(), throwable.getMessage()); - cache.getPlugin().getLogger().severe(s); - if (cache.isDebug()) { - throwable.printStackTrace(); - } - return s; - } - - @Override - public String capture(@Nonnull Throwable throwable, @Nonnull String msg) { - String s = lang.module(this).format("error-specific", cache.getName(), throwable.getMessage(), msg); - cache.getPlugin().getLogger().severe(s); - if (cache.isDebug()) { - throwable.printStackTrace(); - } - return s; + public boolean isDebug() { + return cache.isDebug() || cache.getApi().getPlugin().isDebug(); } - @Override - public String capture(@Nonnull String msg) { - String s = lang.module(this).format("error-generic", cache.getName(), msg); - cache.getPlugin().getLogger().severe(s); - return s; + public String getMsg(String msg) { + return "[" + cache.getName() + "] " + msg; } @Override - public String capture(@Nonnull String module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - cache.getPlugin().getLogger().severe(s); - return s; - } - - @Override - public String capture(@Nonnull Throwable throwable, @Nonnull String module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - cache.getPlugin().getLogger().severe(s); - if (cache.isDebug()) { + public void capture(@Nonnull Throwable throwable) { + cache.getPlugin().getLogger().severe(getMsg(throwable.getMessage())); + if (isDebug()) { throwable.printStackTrace(); } - return s; } - @Override - public String capture(@Nonnull LangModule module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - cache.getPlugin().getLogger().severe(s); - return s; - } @Override - public String capture(@Nonnull Throwable throwable, @Nonnull LangModule module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - cache.getPlugin().getLogger().severe(s); - if (cache.isDebug()) { + public void capture(@Nonnull Throwable throwable, @Nonnull String msg) { + cache.getPlugin().getLogger().severe(msg + " - " + getMsg(throwable.getMessage())); + if (isDebug()) { throwable.printStackTrace(); } - return s; } @Override - public String debug(@Nonnull String msg) { - String s = lang.module(this).format("debug", cache.getName(), msg); - cache.getPlugin().getLogger().info(s); - return s; + public void capture(@Nonnull String msg) { + cache.getPlugin().getLogger().severe(getMsg(msg)); } - @Override - public String debug(@Nonnull String module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - cache.getPlugin().getLogger().info(s); - return s; - } @Override - public String debug(@Nonnull LangModule module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - cache.getPlugin().getLogger().info(s); - return s; + public void debug(@Nonnull String msg) { + cache.getPlugin().getLogger().info("[Debug]" + getMsg(msg)); } } diff --git a/src/main/java/com/jonahseguin/payload/base/error/ErrorService.java b/src/main/java/com/jonahseguin/payload/base/error/ErrorService.java index 44cf32d..bc1f6ca 100644 --- a/src/main/java/com/jonahseguin/payload/base/error/ErrorService.java +++ b/src/main/java/com/jonahseguin/payload/base/error/ErrorService.java @@ -5,31 +5,17 @@ package com.jonahseguin.payload.base.error; -import com.jonahseguin.lang.LangModule; import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface ErrorService { - String capture(@Nonnull Throwable throwable); + void capture(@Nonnull Throwable throwable); - String capture(@Nonnull Throwable throwable, @Nonnull String msg); + void capture(@Nonnull Throwable throwable, @Nonnull String msg); - String capture(@Nonnull String msg); + void capture(@Nonnull String msg); - String capture(@Nonnull String module, @Nonnull String key, @Nullable Object... args); - - String capture(@Nonnull Throwable throwable, @Nonnull String module, @Nonnull String key, @Nullable Object... args); - - String capture(@Nonnull LangModule module, @Nonnull String key, @Nullable Object... args); - - String capture(@Nonnull Throwable throwable, @Nonnull LangModule module, @Nonnull String key, @Nullable Object... args); - - String debug(@Nonnull String msg); - - String debug(@Nonnull String module, @Nonnull String key, @Nullable Object... args); - - String debug(@Nonnull LangModule module, @Nonnull String key, @Nullable Object... args); + void debug(@Nonnull String msg); } diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java b/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java deleted file mode 100644 index 5ee9b10..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/Handshake.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.handshake; - -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.jonahseguin.payload.database.DatabaseService; -import lombok.Getter; -import redis.clients.jedis.Jedis; - -import javax.annotation.Nonnull; - -@Getter -public abstract class Handshake { - - protected final Injector injector; - @Inject - protected HandshakeService handshakeService; - @Inject - protected DatabaseService database; - protected HandshakeListener listener = null; - protected HandshakeHandler handler; - protected Jedis subscriber = null; - - @Inject - public Handshake(Injector injector) { - this.injector = injector; - injector.injectMembers(this); - } - - - public abstract String channelPublish(); - - public abstract String channelReply(); - - public abstract void load(@Nonnull HandshakeData data); - - public abstract void write(@Nonnull HandshakeData data); - - /** - * Called when receiving this handshake from another server, before the reply is sent. Async. - */ - public abstract void receive(); - - public abstract boolean shouldAccept(); - - public boolean shouldReply() { - return true; - } - - void setHandler(HandshakeHandler handshakeHandler) { - this.handler = handshakeHandler; - } - - @SuppressWarnings("unchecked") - void executeHandler() { - if (handler != null) { - handler.call(this); - } - } - - public void listen() { - if (listener == null) { - listener = new HandshakeListener(handshakeService, this); - } - if (subscriber == null) { - subscriber = database.getJedisResource(); - } - if (!listener.isSubscribed()) { - try { - subscriber.subscribe(listener, channelPublish(), channelReply()); - } catch (Exception ex) { - database.getErrorService().capture(ex, "Error during listening for handshake " + this.getClass().getSimpleName()); - ex.printStackTrace(); - } - } - } - - public void stopListening() { - if (listener != null) { - if (listener.isSubscribed()) { - listener.unsubscribe(); - } - } - if (subscriber != null) { - subscriber.close(); - subscriber = null; - } - } - - public abstract Handshake create(); - -} diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeContainer.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeContainer.java deleted file mode 100644 index 83f27d8..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeContainer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.handshake; - -import com.google.common.base.Preconditions; -import lombok.Getter; - -import javax.annotation.Nonnull; - -@Getter -public class HandshakeContainer { - - private final Handshake subscriberController; - - public HandshakeContainer(@Nonnull Handshake subscriber) { - Preconditions.checkNotNull(subscriber); - this.subscriberController = subscriber; - } - - public Handshake createInstance() { - return subscriberController.create(); - } - -} diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java deleted file mode 100644 index f3e76fd..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeData.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.handshake; - -import lombok.Data; -import org.bson.Document; - -import java.util.UUID; - -@Data -public class HandshakeData { - - public static final String ID = "id"; - - private final Document document; - - public String getID() { - return document.getString(ID); - } - - public void writeID() { - document.append(ID, UUID.randomUUID().toString()); - } - - public Document append(String key, Object value) { - return document.append(key, value); - } - -} diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeHandler.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeHandler.java deleted file mode 100644 index 8f56ad7..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.jonahseguin.payload.base.handshake; - -import com.jonahseguin.payload.base.PayloadCallback; -import lombok.Getter; - -import java.util.Optional; - -@Getter -public class HandshakeHandler { - - private volatile H controller = null; - final HandshakeData data; - PayloadCallback callback = object -> { - }; - private boolean timedOut = false; - - public HandshakeHandler(HandshakeData data) { - this.data = data; - } - - void call(H controller) { - this.controller = controller; - if (this.callback != null) { - this.callback.callback(controller); - } - } - - public HandshakeHandler afterReply(PayloadCallback callback) { - this.callback = callback; - return this; - } - - public synchronized Optional waitForReply(int maxSeconds) { - double waited = 0; - while (controller == null) { - if (waited >= maxSeconds) { - timedOut = true; - break; - } - try { - Thread.sleep(100); - waited += 0.1; - } catch (InterruptedException ex) { - this.controller.getDatabase().getErrorService().capture(ex, "Interrupted while waiting for reply in handshake handler"); - } - } - return Optional.ofNullable(controller); - } - -} diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java deleted file mode 100644 index c34ed5d..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.handshake; - -import org.bson.Document; -import redis.clients.jedis.JedisPubSub; - -public class HandshakeListener extends JedisPubSub { - - private final Handshake controller; - private final HandshakeService service; - - public HandshakeListener(HandshakeService service, Handshake controller) { - this.service = service; - this.controller = controller; - } - - @Override - public void onMessage(String channel, String json) { - if (channel.equalsIgnoreCase(controller.channelPublish())) { - // Receiving init. handshake - service.receive(controller.channelPublish(), mapData(json)); - } else if (channel.equalsIgnoreCase(controller.channelReply())) { - // Receiving handshake reply - service.receiveReply(controller.channelReply(), mapData(json)); - } - } - - private HandshakeData mapData(String json) { - Document document = Document.parse(json); - return new HandshakeData(document); - } - -} diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java b/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java deleted file mode 100644 index 10e7742..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/HandshakeService.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.handshake; - -import com.jonahseguin.payload.base.Service; -import com.jonahseguin.payload.database.DatabaseService; - -import javax.annotation.Nonnull; - -public interface HandshakeService extends Service { - - void subscribe(@Nonnull H subscriber); - - HandshakeHandler publish(@Nonnull H packet); - - void receiveReply(@Nonnull String channel, @Nonnull HandshakeData data); - - void receive(@Nonnull String channel, @Nonnull HandshakeData data); - - @Nonnull - String getName(); - - DatabaseService getDatabaseService(); - -} diff --git a/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java b/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java deleted file mode 100644 index f2627b3..0000000 --- a/src/main/java/com/jonahseguin/payload/base/handshake/PayloadHandshakeService.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.handshake; - -import com.google.common.base.Preconditions; -import com.google.inject.Inject; -import com.jonahseguin.payload.database.DatabaseService; -import org.bson.Document; -import redis.clients.jedis.Jedis; - -import javax.annotation.Nonnull; -import java.util.concurrent.*; - -public class PayloadHandshakeService implements HandshakeService { - - private final String name; - private final ConcurrentMap replyControllers = new ConcurrentHashMap<>(); - private final ConcurrentMap containers = new ConcurrentHashMap<>(); - private boolean running = false; - private final DatabaseService database; - private final ExecutorService executor = Executors.newCachedThreadPool(); - - @Inject - public PayloadHandshakeService(DatabaseService database) { - this.database = database; - this.name = database.getName(); - } - - @Override - public boolean start() { - running = true; - return true; - } - - @Override - public boolean isRunning() { - return running; - } - - @Override - public DatabaseService getDatabaseService() { - return database; - } - - @Override - public boolean shutdown() { - this.containers.values().stream() - .map(HandshakeContainer::getSubscriberController) - .forEach(Handshake::stopListening); - shutdownExecutor(); - this.containers.clear(); - this.replyControllers.clear(); - running = false; - return true; - } - - private void shutdownExecutor() { - try { - executor.shutdown(); - executor.awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException ex) { - database.getErrorService().capture(ex, "Interrupted during shutdown of handshake service's executor service"); - } finally { - executor.shutdownNow(); - } - } - - @Override - public void receiveReply(@Nonnull String channel, @Nonnull HandshakeData data) { - Preconditions.checkNotNull(channel); - Preconditions.checkNotNull(data); - if (replyControllers.containsKey(data.getID())) { - Handshake controller = replyControllers.get(data.getID()); - if (controller != null) { - executor.submit(() -> { - controller.load(data); - controller.executeHandler(); - }); - } - replyControllers.remove(data.getID()); - } - } - - @Override - public void receive(@Nonnull String channel, @Nonnull HandshakeData data) { - Preconditions.checkNotNull(channel); - Preconditions.checkNotNull(data); - // Receiving before sending reply - if (containers.containsKey(channel)) { - HandshakeContainer container = containers.get(channel); - Handshake controller = container.createInstance(); - executor.submit(() -> { - controller.load(data); - if (controller.shouldAccept()) { - controller.receive(); - if (controller.shouldReply()) { - try (Jedis jedis = database.getJedisResource()) { - jedis.publish(controller.channelReply(), data.getDocument().toJson()); - } catch (Exception ex) { - database.getErrorService().capture(ex, "Error with Jedis resource during handshake receive (sending reply) for " + controller.getClass().getSimpleName()); - } - } - } - }); - } - } - - @Override - public void subscribe(@Nonnull H subscriber) { - Preconditions.checkNotNull(subscriber); - HandshakeContainer container = new HandshakeContainer(subscriber); - containers.put(subscriber.channelPublish(), container); - containers.put(subscriber.channelReply(), container); - executor.submit(subscriber::listen); - } - - @Override - public HandshakeHandler publish(@Nonnull H controller) { - Preconditions.checkNotNull(controller); - HandshakeData data = new HandshakeData(new Document()); - data.writeID(); - controller.write(data); - HandshakeHandler handler = new HandshakeHandler<>(data); - controller.setHandler(handler); - replyControllers.put(data.getID(), controller); - try (Jedis jedis = database.getJedisResource()) { - jedis.publish(controller.channelPublish(), data.getDocument().toJson()); - } catch (Exception ex) { - database.getErrorService().capture(ex, "Error with Jedis resource during handshake publish for " + controller.getClass().getSimpleName()); - } - return handler; - } - - @Nonnull - @Override - public String getName() { - return name; - } -} diff --git a/src/main/java/com/jonahseguin/payload/base/lang/LangService.java b/src/main/java/com/jonahseguin/payload/base/lang/LangService.java deleted file mode 100644 index df13133..0000000 --- a/src/main/java/com/jonahseguin/payload/base/lang/LangService.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.lang; - -import com.jonahseguin.lang.Lang; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public interface LangService { - - @Nullable - String get(@Nonnull String module, @Nonnull String key, @Nullable Object... args); - - LangDefinitions module(@Nonnull String module); - - LangDefinitions module(@Nonnull LangModule module); - - Lang lang(); - - void register(LangModule module); - -} diff --git a/src/main/java/com/jonahseguin/payload/base/lang/PLang.java b/src/main/java/com/jonahseguin/payload/base/lang/PLang.java new file mode 100644 index 0000000..8dc1fb4 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/base/lang/PLang.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.base.lang; + +public enum PLang { + + NO_PERMISSION("&cNo permission."), + PLAYER_ONLY("&cThis command is player-only."), + INCORRECT_USAGE("&cIncorrect usage. (needs {0} arguments) Use: {1} {2}"), + UNKNOWN_COMMAND("&cUnknown command: '{0}'. Use /payload for help."), + + JOIN_DENY_DATABASE("&c[{0}] The database is currently offline. Please try again soon, we are working to resolve this issue as soon as possible."), + + ; + + private final String lang; + + PLang(String lang) { + this.lang = lang; + } + + public String getLang() { + return lang; + } + + public PLang[] getAll() { + return PLang.values(); + } + + public PLang fromString(String s) { + return PLang.valueOf(s.toUpperCase()); + } + +} diff --git a/src/main/java/com/jonahseguin/payload/base/lang/PLangService.java b/src/main/java/com/jonahseguin/payload/base/lang/PLangService.java new file mode 100644 index 0000000..2b551cd --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/base/lang/PLangService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.base.lang; + +import com.jonahseguin.payload.PayloadPlugin; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.plugin.Plugin; + +import java.util.HashMap; +import java.util.Map; + +@Getter +@Setter +public class PLangService extends PSettings { + + private final Map lang = new HashMap<>(); + + public PLangService(Plugin plugin) { + super(plugin, "lang.yml"); + load(); + + for (PLang lang : PLang.values()) { + if (!this.lang.containsKey(lang)) { + this.lang.put(lang, lang.getLang()); + } + } + + for (PLang key : lang.keySet()) { + if (!getConfig().contains(key.name())) { + getConfig().set(key.name(), lang.get(key)); + } + } + + for (String key : getConfig().getKeys(false)) { + PLang lang = PLang.valueOf(key.toUpperCase()); + this.lang.put(lang, getConfig().getString(key)); + } + save(); + } + + + public String format(PLang lang, Object... args) { + if (!this.lang.containsKey(lang)) { + this.lang.put(lang, lang.getLang()); + } + String val = this.lang.get(lang); + return PayloadPlugin.format(val, args); + } + +} diff --git a/src/main/java/com/jonahseguin/payload/base/lang/PSettings.java b/src/main/java/com/jonahseguin/payload/base/lang/PSettings.java new file mode 100644 index 0000000..80f5be0 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/base/lang/PSettings.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.base.lang; + + +import lombok.Getter; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; + +import java.io.File; +import java.io.IOException; + + +@Getter +public class PSettings { + + protected final YamlConfiguration config; + protected final File file; + protected final File directory; + + public PSettings(Plugin plugin) { + this(plugin, "config.yml"); + } + + public PSettings(Plugin plugin, String filename) { + this(filename, plugin.getDataFolder().getPath()); + } + + public PSettings(String filename, String directory) { + this.directory = new File(directory); + this.file = new File(directory, filename); + config = new YamlConfiguration(); + createFile(); + } + + public void createFile() { + if (!directory.exists()) { + directory.mkdirs(); + } + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void deleteFile() { + if (directory.exists()) { + if (file.exists()) { + file.delete(); + } + } + } + + public void save() { + try { + config.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void load() { + try { + config.load(file); + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + } + } + + public YamlConfiguration getConfig() { + return config; + } +} \ No newline at end of file diff --git a/src/main/java/com/jonahseguin/payload/base/lang/PayloadLangService.java b/src/main/java/com/jonahseguin/payload/base/lang/PayloadLangService.java deleted file mode 100644 index a348e1e..0000000 --- a/src/main/java/com/jonahseguin/payload/base/lang/PayloadLangService.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.lang; - -import com.google.common.base.Preconditions; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.jonahseguin.lang.Lang; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; -import org.bukkit.plugin.Plugin; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -@Singleton -public class PayloadLangService implements LangService { - - private final Lang lang; - - @Inject - public PayloadLangService(@Nonnull Plugin plugin) { - Preconditions.checkNotNull(plugin); - this.lang = new Lang(plugin); - lang.load(); - lang.save(); - } - - @Override - public void register(LangModule module) { - lang.register(module); - } - - @Nullable - @Override - public String get(@Nonnull String module, @Nonnull String key, @Nullable Object... args) { - return lang.module(module).format(key, args); - } - - @Override - public LangDefinitions module(@Nonnull LangModule module) { - return lang.module(module); - } - - @Override - public LangDefinitions module(@Nonnull String module) { - return lang.module(module); - } - - @Override - public Lang lang() { - return lang; - } -} diff --git a/src/main/java/com/jonahseguin/payload/base/listener/LockListener.java b/src/main/java/com/jonahseguin/payload/base/listener/LockListener.java deleted file mode 100644 index 57a5b0f..0000000 --- a/src/main/java/com/jonahseguin/payload/base/listener/LockListener.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.listener; - -import com.google.inject.Inject; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; -import com.jonahseguin.payload.PayloadPlugin; -import com.jonahseguin.payload.base.PayloadPermission; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerLoginEvent; - -public class LockListener implements Listener, LangModule { - - private final PayloadPlugin payloadPlugin; - - @Inject - public LockListener(PayloadPlugin payloadPlugin) { - this.payloadPlugin = payloadPlugin; - payloadPlugin.getLang().register(this); - } - - @Override - public void define(LangDefinitions l) { - l.define("kick", "&cThe server is currently locked for maintenance. Please try again soon. If the server just started up, wait a few seconds for startup to complete and try again."); - l.define("bypassed", "&c[Payload] Locked for maintenance, but you bypassed this lock because you have admin privileges."); - } - - @Override - public String langModule() { - return "lock"; - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onLoginPayloadLocked(PlayerLoginEvent event) { - if (payloadPlugin.isLocked()) { - Player player = event.getPlayer(); - if (!PayloadPermission.ADMIN.has(player)) { - event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, payloadPlugin.getLang().module(this).format("kick")); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onJoinPayloadLocked(PlayerJoinEvent event) { - if (payloadPlugin.isLocked()) { - Player player = event.getPlayer(); - if (PayloadPermission.ADMIN.has(player)) { - player.sendMessage(" "); - player.sendMessage(" "); - player.sendMessage(payloadPlugin.getLang().module(this).format("bypassed")); - } - } - } - -} diff --git a/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java b/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java deleted file mode 100644 index f70141a..0000000 --- a/src/main/java/com/jonahseguin/payload/base/network/NetworkPayload.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.network; - -import com.google.inject.Inject; -import com.jonahseguin.payload.server.ServerService; -import dev.morphia.annotations.Embedded; -import dev.morphia.annotations.Entity; -import dev.morphia.annotations.Id; -import dev.morphia.annotations.Transient; -import lombok.Getter; -import lombok.Setter; -import org.bson.types.ObjectId; - -import javax.annotation.Nonnull; - -@Getter -@Setter -@Entity -public abstract class NetworkPayload { - - @Id - private ObjectId id = new ObjectId(); // required for morphia mapping - - @Transient - protected transient final ServerService serverService; - protected long lastCached = System.currentTimeMillis(); - protected long lastSaved = System.currentTimeMillis(); - protected boolean loaded = false; - @Embedded - protected String mostRecentServer; - - @Inject - public NetworkPayload(ServerService serverService) { - this.serverService = serverService; - } - - public boolean isThisMostRelevantServer() { - if (mostRecentServer != null) { - return mostRecentServer.equalsIgnoreCase(serverService.getThisServer().getName()) && loaded; - } - return false; - } - - public abstract K getIdentifier(); - - public abstract void setIdentifier(@Nonnull K identifier); - -} diff --git a/src/main/java/com/jonahseguin/payload/base/network/NetworkService.java b/src/main/java/com/jonahseguin/payload/base/network/NetworkService.java deleted file mode 100644 index 4f38bfa..0000000 --- a/src/main/java/com/jonahseguin/payload/base/network/NetworkService.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.network; - -import com.jonahseguin.payload.base.Service; -import com.jonahseguin.payload.base.type.Payload; - -import javax.annotation.Nonnull; -import java.util.Optional; - -public interface NetworkService, N extends NetworkPayload> extends Service { - - Optional get(@Nonnull K key); - - Optional get(@Nonnull X payload); - - boolean has(@Nonnull K key); - - boolean save(@Nonnull N payload); - - Optional get(@Nonnull N payload); - - N create(@Nonnull X payload); - - -} diff --git a/src/main/java/com/jonahseguin/payload/base/network/RedisNetworkService.java b/src/main/java/com/jonahseguin/payload/base/network/RedisNetworkService.java deleted file mode 100644 index f7a6f82..0000000 --- a/src/main/java/com/jonahseguin/payload/base/network/RedisNetworkService.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.network; - -import com.google.common.base.Preconditions; -import com.google.inject.Inject; -import com.jonahseguin.payload.base.PayloadCache; -import com.jonahseguin.payload.base.type.Payload; -import com.jonahseguin.payload.database.DatabaseService; -import com.mongodb.BasicDBObject; -import redis.clients.jedis.Jedis; - -import javax.annotation.Nonnull; -import java.util.Optional; - -public class RedisNetworkService, N extends NetworkPayload, D> implements NetworkService { - - private final PayloadCache cache; - private final DatabaseService database; - private final Class type; - private boolean running = false; - - @Inject - public RedisNetworkService(PayloadCache cache, Class type, DatabaseService database) { - this.cache = cache; - this.database = database; - this.type = type; - } - - @Override - public Optional get(@Nonnull K key) { - Preconditions.checkNotNull(key); - try (Jedis jedis = database.getJedisResource()) { - String json = jedis.hget(cache.getServerSpecificName(), cache.keyToString(key)); - if (json != null) { - BasicDBObject dbObject = BasicDBObject.parse(json); - N np = database.getMorphia().fromDBObject(database.getDatastore(), type, dbObject); - return Optional.ofNullable(np); - } - } catch (Exception ex) { - cache.getErrorService().capture(ex, "Error getting network payload in Redis Network Service"); - } - return Optional.empty(); - } - - @Override - public Optional get(@Nonnull X payload) { - Preconditions.checkNotNull(payload); - try (Jedis jedis = database.getJedisResource()) { - String json = jedis.hget(cache.getServerSpecificName(), cache.keyToString(payload.getIdentifier())); - if (json != null && json.length() > 0) { - BasicDBObject dbObject = BasicDBObject.parse(json); - N np = database.getMorphia().fromDBObject(database.getDatastore(), type, dbObject); - if (np != null) { - np.setIdentifier(payload.getIdentifier()); - } - return Optional.ofNullable(np); - } else { - N np = create(payload); - if (save(np)) { - return Optional.of(np); - } else { - cache.getErrorService().capture("Failed to save after creation of network payload " + cache.keyToString(payload.getIdentifier())); - } - } - } catch (Exception ex) { - cache.getErrorService().capture(ex, "Error getting network payload in Redis Network Service"); - } - return Optional.empty(); - } - - @Override - public boolean has(@Nonnull K key) { - Preconditions.checkNotNull(key); - try (Jedis jedis = database.getJedisResource()) { - return jedis.hexists(cache.getServerSpecificName(), cache.keyToString(key)); - } catch (Exception ex) { - cache.getErrorService().capture(ex, "Error checking if hexists() in Redis Network Service: " + cache.keyToString(key)); - } - return false; - } - - @Override - public boolean save(@Nonnull N payload) { - Preconditions.checkNotNull(payload); - BasicDBObject object = (BasicDBObject) database.getMorphia().toDBObject(payload); - if (object != null) { - String json = object.toJson(); - Preconditions.checkNotNull(json, "JSON is null"); - Preconditions.checkNotNull(cache.getServerSpecificName(), "Server specific cache name is null"); - Preconditions.checkNotNull(payload.getIdentifier()); - Preconditions.checkNotNull(cache.keyToString(payload.getIdentifier()), "Payload identifier key is null"); - try (Jedis jedis = database.getJedisResource()) { - jedis.hset(cache.getServerSpecificName(), cache.keyToString(payload.getIdentifier()), json); - return true; - } catch (Exception ex) { - cache.getErrorService().capture(ex, "Error saving in Redis Network Service: " + cache.keyToString(payload.getIdentifier())); - } - } - return false; - } - - @Override - public Optional get(@Nonnull N payload) { - Preconditions.checkNotNull(payload); - return cache.get(payload.getIdentifier()); - } - - @Override - public N create(@Nonnull X payload) { - Preconditions.checkNotNull(payload); - N np = cache.createNetworked(); - np.setIdentifier(payload.getIdentifier()); - return np; - } - - @Override - public boolean start() { - running = true; - return true; - } - - @Override - public boolean shutdown() { - running = false; - return true; - } - - @Override - public boolean isRunning() { - return running; - } -} diff --git a/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java b/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java index d722a81..e5eb021 100644 --- a/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java +++ b/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java @@ -5,7 +5,6 @@ package com.jonahseguin.payload.base.service; -import com.jonahseguin.payload.mode.object.NetworkObject; import com.jonahseguin.payload.mode.object.ObjectCache; import com.jonahseguin.payload.mode.object.PayloadObject; diff --git a/src/main/java/com/jonahseguin/payload/base/service/PayloadProfileService.java b/src/main/java/com/jonahseguin/payload/base/service/PayloadProfileService.java index c6fc43e..c46a9ec 100644 --- a/src/main/java/com/jonahseguin/payload/base/service/PayloadProfileService.java +++ b/src/main/java/com/jonahseguin/payload/base/service/PayloadProfileService.java @@ -5,7 +5,6 @@ package com.jonahseguin.payload.base.service; -import com.jonahseguin.payload.mode.profile.NetworkProfile; import com.jonahseguin.payload.mode.profile.PayloadProfile; import com.jonahseguin.payload.mode.profile.ProfileCache; import org.bukkit.entity.Player; @@ -14,18 +13,13 @@ import java.util.Collection; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.Future; -public interface PayloadProfileService extends PayloadService { +public interface PayloadProfileService extends PayloadService { Optional get(@Nonnull String username); Optional get(@Nonnull Player player); - Future> getAsync(@Nonnull String username); - - Future> getAsync(@Nonnull Player player); - Optional getFromCache(@Nonnull String username); Optional getFromCache(@Nonnull Player player); diff --git a/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java b/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java index c16d165..ad5ea81 100644 --- a/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java +++ b/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java @@ -7,7 +7,6 @@ import com.jonahseguin.payload.base.PayloadCallback; import com.jonahseguin.payload.base.Service; -import com.jonahseguin.payload.base.network.NetworkPayload; import com.jonahseguin.payload.base.type.Payload; import javax.annotation.Nonnull; @@ -15,11 +14,7 @@ import java.util.Optional; import java.util.concurrent.Future; -public interface PayloadService, N extends NetworkPayload> extends Service { - - Optional getNetworked(@Nonnull K key); - - Optional getNetworked(@Nonnull X payload); +public interface PayloadService> extends Service { Optional get(@Nonnull K key); @@ -55,8 +50,6 @@ public interface PayloadService, N extends NetworkPayloa X create(); - N createNetworked(); - int saveAll(); Collection all(); diff --git a/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java b/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java index 59b5c5a..0aaacac 100644 --- a/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java +++ b/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java @@ -14,9 +14,7 @@ @NoArgsConstructor public abstract class CacheSettings { - private int failureRetryIntervalSeconds = 30; - private int autoSaveIntervalSeconds = 600; // 10 minutes - private int cleanupIntervalSeconds = 1200; + private int autoSaveIntervalSeconds = 600; private boolean serverSpecific = false; // should we associate each object with a server, and only cache objects that match this server - private boolean enableSync = true; // Enable the payload sync service. This will sync objects/profiles (Payloads) across multiple servers, in a policy specific to the SyncMode + } diff --git a/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java b/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java deleted file mode 100644 index 6fbabec..0000000 --- a/src/main/java/com/jonahseguin/payload/base/sync/CacheSyncService.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.sync; - -import com.google.common.base.Preconditions; -import com.jonahseguin.payload.base.Cache; -import com.jonahseguin.payload.base.PayloadCallback; -import com.jonahseguin.payload.base.handshake.HandshakeService; -import com.jonahseguin.payload.base.network.NetworkPayload; -import com.jonahseguin.payload.base.type.Payload; - -import javax.annotation.Nonnull; -import java.util.Optional; - -public class CacheSyncService, N extends NetworkPayload> implements SyncService { - - private final Cache cache; - private final HandshakeService handshakeService; - private boolean running = false; - - public CacheSyncService(Cache cache, HandshakeService handshakeService) { - this.cache = cache; - this.handshakeService = handshakeService; - } - - @Override - public void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback> callback) { - Preconditions.checkNotNull(payload); - Preconditions.checkNotNull(callback); - Optional o = cache.getNetworked(payload); - if (o.isPresent()) { - N np = o.get(); - if (np.isThisMostRelevantServer()) { - callback.callback(Optional.of(payload)); - } else { - handshakeService.publish(new SyncHandshake<>(cache.getInjector(), cache, payload.getIdentifier(), SyncHandshakeMode.UPDATE)).afterReply(h -> { - Optional ox = cache.getFromDatabase(payload.getIdentifier()); - callback.callback(ox); - }); - } - } else { - callback.callback(Optional.empty()); - } - } - - @Override - public void update(@Nonnull K key) { - Preconditions.checkNotNull(key); - handshakeService.publish(new SyncHandshake<>(cache.getInjector(), cache, key, SyncHandshakeMode.UPDATE)); - } - - @Override - public void uncache(@Nonnull K key) { - Preconditions.checkNotNull(key); - handshakeService.publish(new SyncHandshake<>(cache.getInjector(), cache, key, SyncHandshakeMode.UNCACHE)); - } - - @Override - public boolean start() { - running = true; - handshakeService.subscribe(new SyncHandshake<>(cache.getInjector(), cache)); - return true; - } - - @Override - public boolean shutdown() { - running = false; - return true; - } - - @Override - public boolean isRunning() { - return running; - } -} diff --git a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java b/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java deleted file mode 100644 index d155e6f..0000000 --- a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshake.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.sync; - -import com.google.common.base.Preconditions; -import com.google.inject.Injector; -import com.jonahseguin.payload.base.Cache; -import com.jonahseguin.payload.base.handshake.Handshake; -import com.jonahseguin.payload.base.handshake.HandshakeData; -import com.jonahseguin.payload.base.network.NetworkPayload; -import com.jonahseguin.payload.base.type.Payload; -import lombok.Getter; -import lombok.Setter; - -import javax.annotation.Nonnull; - -@Getter -@Setter -public class SyncHandshake, N extends NetworkPayload> extends Handshake { - - public static final String KEY_IDENTIFIER = "sync-identifier"; - public static final String KEY_MODE = "sync-mode"; - private final Cache cache; - private K identifier; - private SyncHandshakeMode mode; - - public SyncHandshake(Injector injector, Cache cache) { - super(injector); - this.cache = cache; - } - - public SyncHandshake(Injector injector, Cache cache, @Nonnull K identifier, @Nonnull SyncHandshakeMode mode) { - super(injector); - Preconditions.checkNotNull(identifier); - this.cache = cache; - this.identifier = identifier; - this.mode = mode; - } - - @Override - public SyncHandshake create() { - return new SyncHandshake<>(injector, cache); - } - - @Override - public String channelPublish() { - return "payload-sync-" + cache.getName(); - } - - @Override - public String channelReply() { - return "payload-sync-" + cache.getName() + "-reply"; - } - - @Override - public void load(@Nonnull HandshakeData data) { - identifier = cache.keyFromString(data.getDocument().getString(KEY_IDENTIFIER)); - mode = SyncHandshakeMode.valueOf(data.getDocument().getString(KEY_MODE)); - } - - @Override - public void write(@Nonnull HandshakeData data) { - data.append(KEY_IDENTIFIER, cache.keyToString(identifier)); - data.append(KEY_MODE, mode.name()); - } - - @Override - public void receive() { - if (mode.equals(SyncHandshakeMode.UNCACHE)) { - if (cache.isCached(identifier)) { - cache.uncache(identifier); - } - } else if (mode.equals(SyncHandshakeMode.UPDATE)) { - if (cache.isCached(identifier) || cache.getSyncMode().equals(SyncMode.ALWAYS)) { - cache.getFromDatabase(identifier).ifPresent(cache::cache); - } - } - } - - @Override - public boolean shouldAccept() { - return cache.isCached(identifier) || cache.getSyncMode().equals(SyncMode.ALWAYS); - } - - @Override - public boolean shouldReply() { - return mode.equals(SyncHandshakeMode.UPDATE); // only send a reply if its an update handshake. uncache isn't a handshake - } -} diff --git a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshakeMode.java b/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshakeMode.java deleted file mode 100644 index 742cdf6..0000000 --- a/src/main/java/com/jonahseguin/payload/base/sync/SyncHandshakeMode.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.sync; - -public enum SyncHandshakeMode { - - UPDATE, - UNCACHE - -} diff --git a/src/main/java/com/jonahseguin/payload/base/sync/SyncMode.java b/src/main/java/com/jonahseguin/payload/base/sync/SyncMode.java deleted file mode 100644 index c8f45af..0000000 --- a/src/main/java/com/jonahseguin/payload/base/sync/SyncMode.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.sync; - -public enum SyncMode { - - ALWAYS, - IF_CACHED - -} diff --git a/src/main/java/com/jonahseguin/payload/base/sync/SyncService.java b/src/main/java/com/jonahseguin/payload/base/sync/SyncService.java deleted file mode 100644 index 8debbfd..0000000 --- a/src/main/java/com/jonahseguin/payload/base/sync/SyncService.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.base.sync; - -import com.jonahseguin.payload.base.PayloadCallback; -import com.jonahseguin.payload.base.Service; -import com.jonahseguin.payload.base.network.NetworkPayload; -import com.jonahseguin.payload.base.type.Payload; - -import javax.annotation.Nonnull; -import java.util.Optional; - -public interface SyncService, N extends NetworkPayload> extends Service { - - void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback> callback); - - void update(@Nonnull K key); - - void uncache(@Nonnull K key); - -} diff --git a/src/main/java/com/jonahseguin/payload/base/task/PayloadAutoSaveTask.java b/src/main/java/com/jonahseguin/payload/base/task/PayloadAutoSaveTask.java index d5ee81e..d681d9f 100644 --- a/src/main/java/com/jonahseguin/payload/base/task/PayloadAutoSaveTask.java +++ b/src/main/java/com/jonahseguin/payload/base/task/PayloadAutoSaveTask.java @@ -7,19 +7,18 @@ import com.google.common.base.Preconditions; import com.jonahseguin.payload.base.Cache; -import com.jonahseguin.payload.base.network.NetworkPayload; import com.jonahseguin.payload.base.type.Payload; import org.bukkit.scheduler.BukkitTask; import javax.annotation.Nonnull; -public class PayloadAutoSaveTask, N extends NetworkPayload> implements Runnable { +public class PayloadAutoSaveTask> implements Runnable { - private final Cache cache; + private final Cache cache; private BukkitTask task = null; - public PayloadAutoSaveTask(@Nonnull Cache cache) { + public PayloadAutoSaveTask(@Nonnull Cache cache) { Preconditions.checkNotNull(cache); this.cache = cache; } diff --git a/src/main/java/com/jonahseguin/payload/command/PCommandHandler.java b/src/main/java/com/jonahseguin/payload/command/PCommandHandler.java index 03f13aa..79393e1 100644 --- a/src/main/java/com/jonahseguin/payload/command/PCommandHandler.java +++ b/src/main/java/com/jonahseguin/payload/command/PCommandHandler.java @@ -8,11 +8,10 @@ import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.google.inject.Injector; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; import com.jonahseguin.payload.PayloadPlugin; import com.jonahseguin.payload.base.PayloadPermission; -import com.jonahseguin.payload.base.lang.LangService; +import com.jonahseguin.payload.base.lang.PLang; +import com.jonahseguin.payload.base.lang.PLangService; import com.jonahseguin.payload.command.commands.*; import lombok.Getter; import org.bukkit.command.Command; @@ -26,21 +25,19 @@ import java.util.Map; @Getter -public class PCommandHandler implements CommandExecutor, LangModule { +public class PCommandHandler implements CommandExecutor { private final PayloadPlugin plugin; - private final LangService langService; - private final LangDefinitions lang; private final Map commands = new HashMap<>(); + private final PLangService lang; @Inject - public PCommandHandler(@Nonnull PayloadPlugin plugin, @Nonnull LangService langService, @Nonnull Injector injector) { + public PCommandHandler(@Nonnull PayloadPlugin plugin, @Nonnull PLangService lang, @Nonnull Injector injector) { Preconditions.checkNotNull(plugin); - Preconditions.checkNotNull(langService); + Preconditions.checkNotNull(lang); Preconditions.checkNotNull(injector); this.plugin = plugin; - this.langService = langService; - this.lang = langService.module(this); + this.lang = lang; register(injector.getInstance(CmdHelp.class)); register(injector.getInstance(CmdCache.class)); register(injector.getInstance(CmdCacheList.class)); @@ -55,19 +52,6 @@ public PCommandHandler(@Nonnull PayloadPlugin plugin, @Nonnull LangService langS register(injector.getInstance(CmdServers.class)); } - @Override - public void define(LangDefinitions l) { - l.define("no-permission", "&cNo permission."); - l.define("player-only", "&cThis command is player-only."); - l.define("incorrect-usage", "&cIncorrect usage. (needs {0} arguments) Use: {1} {2}"); - l.define("unknown", "&cUnknown command: '{0}'. Use /payload for help."); - } - - @Override - public String langModule() { - return "cmd"; - } - private void register(PayloadCommand cmd) { this.commands.put(cmd.name().toLowerCase(), cmd); } @@ -106,15 +90,15 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } if (command != null) { if (command.playerOnly() && !(sender instanceof Player)) { - sender.sendMessage(lang.format("player-only")); + sender.sendMessage(lang.format(PLang.PLAYER_ONLY)); return true; } if (!command.permission().has(sender)) { - sender.sendMessage(lang.format("no-permission")); + sender.sendMessage(lang.format(PLang.NO_PERMISSION)); return true; } if (args.length < command.minArgs()) { - sender.sendMessage(lang.format("incorrect-usage", command.minArgs() + "", "/" + command.name() + " " + command.usage())); + sender.sendMessage(lang.format(PLang.INCORRECT_USAGE, command.minArgs() + "", "/" + command.name() + " " + command.usage())); return true; } CmdArgs cmdArgs = new CmdArgs(sender, pCmd, args); @@ -129,7 +113,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } } } else { - sender.sendMessage(lang.format("unknown", pCmd)); + sender.sendMessage(lang.format(PLang.UNKNOWN_COMMAND, pCmd)); } return true; diff --git a/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java b/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java index 4b16109..81ee0fa 100644 --- a/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java +++ b/src/main/java/com/jonahseguin/payload/command/commands/CmdProfile.java @@ -11,9 +11,9 @@ import com.jonahseguin.payload.base.PayloadPermission; import com.jonahseguin.payload.command.CmdArgs; import com.jonahseguin.payload.command.PayloadCommand; -import com.jonahseguin.payload.mode.profile.NetworkProfile; import com.jonahseguin.payload.mode.profile.PayloadProfile; import com.jonahseguin.payload.mode.profile.PayloadProfileCache; +import com.jonahseguin.payload.mode.profile.network.NetworkProfile; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -61,7 +61,6 @@ public void execute(CmdArgs args) { NetworkProfile np = onp.get(); args.msg("&eNetwork Properties:"); args.msg("&7Online: &6{0}", (np.isOnline() ? "&aYes" : "&cNo")); - args.msg("&7Loaded: &6{0}", (np.isLoaded() ? "&aYes" : "&cNo")); args.msg("&7Last Seen On: &6{0}", np.getLastSeenServer() != null ? np.getLastSeenServer() : "&cN/A"); args.msg("&7Last Seen At: &6{0}", np.getLastSeen() > 0 ? formatDateTime(np.getLastSeen()) : "&cN/A"); args.msg("&7Last Saved: &6{0}", np.getLastSaved() > 0 ? formatDateTime(np.getLastSaved()) : "&cN/A"); diff --git a/src/main/java/com/jonahseguin/payload/database/DatabaseDependent.java b/src/main/java/com/jonahseguin/payload/database/DatabaseDependent.java index a9e5187..21508ac 100644 --- a/src/main/java/com/jonahseguin/payload/database/DatabaseDependent.java +++ b/src/main/java/com/jonahseguin/payload/database/DatabaseDependent.java @@ -1,43 +1,12 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + package com.jonahseguin.payload.database; public interface DatabaseDependent { - /** - * Called when MongoDB loses an active connection - */ - default void onMongoDbDisconnect() { - } - - /** - * Called when Redis loses an active connection - */ - default void onRedisDisconnect() { - } - - /** - * Called when MongoDB regains a connection after it was previously lost - */ - default void onMongoDbReconnect() { - } - - /** - * Called when Redis regains a connection after it was previously lost - */ - default void onRedisReconnect() { - } - - /** - * Called when MongoDB connects for this first time (ex. during startup) - */ - default void onMongoDbInitConnect() { - } - - /** - * Called when Redis connects for this first time (ex. during startup) - */ - default void onRedisInitConnect() { - } - /** * Does this database-dependent object depend on Redis for functionality? * @return True if required diff --git a/src/main/java/com/jonahseguin/payload/database/DatabaseErrorService.java b/src/main/java/com/jonahseguin/payload/database/DatabaseErrorService.java deleted file mode 100644 index 350511a..0000000 --- a/src/main/java/com/jonahseguin/payload/database/DatabaseErrorService.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.database; - -import com.google.inject.Inject; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; -import com.jonahseguin.payload.PayloadPlugin; -import com.jonahseguin.payload.annotation.Database; -import com.jonahseguin.payload.base.PayloadPermission; -import com.jonahseguin.payload.base.error.ErrorService; -import com.jonahseguin.payload.base.lang.LangService; -import org.bukkit.plugin.Plugin; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public class DatabaseErrorService implements ErrorService, LangModule { - - protected final String name; - protected final LangService lang; - protected final Plugin plugin; - protected final PayloadPlugin payloadPlugin; - - @Inject - public DatabaseErrorService(@Database String name, LangService lang, Plugin plugin, PayloadPlugin payloadPlugin) { - this.name = name; - this.lang = lang; - this.plugin = plugin; - this.payloadPlugin = payloadPlugin; - lang.register(this); - } - - @Override - public void define(LangDefinitions l) { - l.define("error-generic", "&7[Payload][&cError&7][{0}] {1}"); - l.define("error-specific", "&7[Payload][&cError&7][{0}] {1} - {2}"); - l.define("debug", "&7[Payload][&eDebug&7][{0}] {1}"); - } - - @Override - public String langModule() { - return "database"; - } - - @Override - public String capture(@Nonnull Throwable throwable) { - String s = lang.module(this).format("error-generic", name, throwable.getMessage()); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - throwable.printStackTrace(); - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String capture(@Nonnull Throwable throwable, @Nonnull String msg) { - String s = lang.module(this).format("error-specific", name, throwable.getMessage(), msg); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - throwable.printStackTrace(); - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String capture(@Nonnull String msg) { - String s = lang.module(this).format("error-generic", name, msg); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String capture(@Nonnull String module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String capture(@Nonnull Throwable throwable, @Nonnull String module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - throwable.printStackTrace(); - } - return s; - } - - @Override - public String capture(@Nonnull LangModule module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String capture(@Nonnull Throwable throwable, @Nonnull LangModule module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - plugin.getLogger().severe(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - throwable.printStackTrace(); - } - return s; - } - - @Override - public String debug(@Nonnull String msg) { - String s = lang.module(this).format("debug", name, msg); - plugin.getLogger().info(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String debug(@Nonnull String module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - plugin.getLogger().info(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } - - @Override - public String debug(@Nonnull LangModule module, @Nonnull String key, @Nullable Object... args) { - String s = lang.module(module).format(key, args); - plugin.getLogger().info(s); - if (payloadPlugin.isDebug()) { - payloadPlugin.alert(PayloadPermission.DEBUG, s); - } - return s; - } -} diff --git a/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java b/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java index ff9b8c8..627e917 100644 --- a/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java +++ b/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java @@ -15,6 +15,9 @@ import com.jonahseguin.payload.base.error.ErrorService; import com.jonahseguin.payload.base.handshake.HandshakeService; import com.jonahseguin.payload.base.handshake.PayloadHandshakeService; +import com.jonahseguin.payload.database.error.DatabaseErrorService; +import com.jonahseguin.payload.database.internal.InternalPayloadDatabase; +import com.jonahseguin.payload.database.internal.PayloadDatabaseService; import com.jonahseguin.payload.server.PayloadServerService; import com.jonahseguin.payload.server.ServerService; diff --git a/src/main/java/com/jonahseguin/payload/database/DatabaseService.java b/src/main/java/com/jonahseguin/payload/database/DatabaseService.java index b248c68..53931f8 100644 --- a/src/main/java/com/jonahseguin/payload/database/DatabaseService.java +++ b/src/main/java/com/jonahseguin/payload/database/DatabaseService.java @@ -12,8 +12,9 @@ import com.mongodb.client.MongoDatabase; import dev.morphia.Datastore; import dev.morphia.Morphia; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; import javax.annotation.Nonnull; import java.util.Set; @@ -28,11 +29,11 @@ public interface DatabaseService extends Service { MongoDatabase getDatabase(); - Jedis getJedisResource(); + RedisClient getRedisClient(); - Jedis getMonitorJedis(); + StatefulRedisConnection getRedis(); - JedisPool getJedisPool(); + StatefulRedisPubSubConnection getRedisPubSub(); ServerService getServerService(); diff --git a/src/main/java/com/jonahseguin/payload/database/PayloadDatabase.java b/src/main/java/com/jonahseguin/payload/database/PayloadDatabase.java index 47a524c..eebf2d4 100644 --- a/src/main/java/com/jonahseguin/payload/database/PayloadDatabase.java +++ b/src/main/java/com/jonahseguin/payload/database/PayloadDatabase.java @@ -14,9 +14,10 @@ import com.jonahseguin.payload.server.ServerService; import com.mongodb.MongoClient; import com.mongodb.client.MongoDatabase; +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; import org.bukkit.plugin.Plugin; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; import java.util.Set; @@ -54,10 +55,12 @@ public interface PayloadDatabase { PayloadRedis getPayloadRedis(); - JedisPool getJedisPool(); + PayloadRedisMonitor getRedisMonitor(); - Jedis getMonitorJedis(); + StatefulRedisConnection getRedis(); - PayloadRedisMonitor getRedisMonitor(); + StatefulRedisPubSubConnection getRedisPubSub(); + + RedisClient getRedisClient(); } diff --git a/src/main/java/com/jonahseguin/payload/database/error/DatabaseErrorService.java b/src/main/java/com/jonahseguin/payload/database/error/DatabaseErrorService.java new file mode 100644 index 0000000..b6dbdef --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/database/error/DatabaseErrorService.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.database.error; + +import com.google.inject.Inject; +import com.jonahseguin.payload.PayloadPlugin; +import com.jonahseguin.payload.annotation.Database; +import com.jonahseguin.payload.base.error.ErrorService; +import org.bukkit.plugin.Plugin; + +import javax.annotation.Nonnull; + +public class DatabaseErrorService implements ErrorService { + + protected final String name; + protected final Plugin plugin; + protected final PayloadPlugin payloadPlugin; + + @Inject + public DatabaseErrorService(@Database String name, Plugin plugin, PayloadPlugin payloadPlugin) { + this.name = name; + this.plugin = plugin; + this.payloadPlugin = payloadPlugin; + } + + public String getMsg(String msg, boolean debug) { + return "[Database: " + name + "]" + (debug ? "[Debug]" : "") + " " + msg; + } + + public boolean isDebug() { + return payloadPlugin.isDebug(); + } + + @Override + public void capture(@Nonnull Throwable throwable) { + plugin.getLogger().severe(getMsg(throwable.getMessage(), false)); + if (isDebug()) { + throwable.printStackTrace(); + } + } + + @Override + public void capture(@Nonnull Throwable throwable, @Nonnull String msg) { + plugin.getLogger().severe(getMsg(msg + " - " + throwable.getMessage(), false)); + if (payloadPlugin.isDebug()) { + throwable.printStackTrace(); + } + } + + @Override + public void capture(@Nonnull String msg) { + plugin.getLogger().severe(getMsg(msg, false)); + } + + @Override + public void debug(@Nonnull String msg) { + plugin.getLogger().info(getMsg(msg, true)); + } + +} diff --git a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java b/src/main/java/com/jonahseguin/payload/database/internal/InternalPayloadDatabase.java similarity index 90% rename from src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java rename to src/main/java/com/jonahseguin/payload/database/internal/InternalPayloadDatabase.java index 75a944d..f10827c 100644 --- a/src/main/java/com/jonahseguin/payload/database/InternalPayloadDatabase.java +++ b/src/main/java/com/jonahseguin/payload/database/internal/InternalPayloadDatabase.java @@ -3,7 +3,7 @@ * www.jonahseguin.com */ -package com.jonahseguin.payload.database; +package com.jonahseguin.payload.database.internal; import com.google.common.base.Preconditions; import com.google.inject.Inject; @@ -13,6 +13,9 @@ import com.jonahseguin.payload.annotation.Database; import com.jonahseguin.payload.base.error.ErrorService; import com.jonahseguin.payload.base.exception.runtime.PayloadConfigException; +import com.jonahseguin.payload.database.DatabaseDependent; +import com.jonahseguin.payload.database.DatabaseState; +import com.jonahseguin.payload.database.PayloadDatabase; import com.jonahseguin.payload.database.mongo.PayloadMongo; import com.jonahseguin.payload.database.mongo.PayloadMongoMonitor; import com.jonahseguin.payload.database.redis.PayloadRedis; @@ -20,18 +23,17 @@ import com.jonahseguin.payload.server.ServerService; import com.mongodb.*; import com.mongodb.client.MongoDatabase; +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; import lombok.Getter; import lombok.Setter; -import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; import java.io.*; -import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashSet; @@ -60,8 +62,9 @@ public class InternalPayloadDatabase implements PayloadDatabase { // Redis private PayloadRedis payloadRedis = null; - private JedisPool jedisPool = null; - private Jedis monitorJedis = null; + private RedisClient redisClient = null; + private StatefulRedisConnection redis = null; + private StatefulRedisPubSubConnection redisPubSub = null; private PayloadRedisMonitor redisMonitor = null; @Inject @@ -174,27 +177,16 @@ public boolean connectRedis() { this.state.setLastRedisConnectionAttempt(System.currentTimeMillis()); // Try connection - if (this.jedisPool == null) { - - GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); - poolConfig.setMaxTotal(64); - poolConfig.setMaxIdle(16); - poolConfig.setMinIdle(8); + if (redisClient == null) { + redisClient = RedisClient.create(payloadRedis.getRedisURI()); + } - if (payloadRedis.useURI()) { - jedisPool = new JedisPool(poolConfig, URI.create(payloadRedis.getUri())); - } else { - if (payloadRedis.isAuth()) { - jedisPool = new JedisPool(poolConfig, payloadRedis.getAddress(), payloadRedis.getPort(), 2000, payloadRedis.getPassword(), payloadRedis.isSsl()); - } else { - jedisPool = new JedisPool(poolConfig, payloadRedis.getAddress(), payloadRedis.getPort()); - } - } + if (redis == null) { + redis = redisClient.connect(); } - if (this.monitorJedis == null) { - this.monitorJedis = this.jedisPool.getResource(); - this.monitorJedis.ping(); + if (redisPubSub == null) { + redisPubSub = redisClient.connectPubSub(); } return true; // No errors if we got here; success @@ -223,7 +215,16 @@ private boolean disconnectRedis() { if (this.redisMonitor != null) { this.redisMonitor.stop(); } - this.jedisPool.close(); + if (this.redis != null) { + if (this.redis.isOpen()) { + this.redis.close(); + } + this.redis = null; + } + if (this.redisClient != null) { + this.redisClient.shutdown(); + this.redisClient = null; + } return true; } diff --git a/src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java b/src/main/java/com/jonahseguin/payload/database/internal/PayloadDatabaseService.java similarity index 83% rename from src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java rename to src/main/java/com/jonahseguin/payload/database/internal/PayloadDatabaseService.java index 8461170..f1cfc3c 100644 --- a/src/main/java/com/jonahseguin/payload/database/PayloadDatabaseService.java +++ b/src/main/java/com/jonahseguin/payload/database/internal/PayloadDatabaseService.java @@ -3,7 +3,7 @@ * www.jonahseguin.com */ -package com.jonahseguin.payload.database; +package com.jonahseguin.payload.database.internal; import com.google.common.base.Preconditions; import com.google.inject.Inject; @@ -11,14 +11,19 @@ import com.google.inject.Singleton; import com.jonahseguin.payload.annotation.Database; import com.jonahseguin.payload.base.error.ErrorService; +import com.jonahseguin.payload.database.DatabaseDependent; +import com.jonahseguin.payload.database.DatabaseService; +import com.jonahseguin.payload.database.DatabaseState; +import com.jonahseguin.payload.database.PayloadDatabase; import com.jonahseguin.payload.server.ServerService; import com.mongodb.MongoClient; import com.mongodb.client.MongoDatabase; import dev.morphia.Datastore; import dev.morphia.Morphia; import dev.morphia.ext.guice.GuiceExtension; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; import javax.annotation.Nonnull; import java.util.Set; @@ -48,6 +53,11 @@ public boolean isRunning() { return database.isRunning(); } + @Override + public StatefulRedisPubSubConnection getRedisPubSub() { + return database.getRedisPubSub(); + } + @Override public MongoClient getMongoClient() { return database.getMongoClient(); @@ -64,18 +74,18 @@ public MongoDatabase getDatabase() { } @Override - public Datastore getDatastore() { - return datastore; + public RedisClient getRedisClient() { + return database.getRedisClient(); } @Override - public Jedis getJedisResource() { - return database.getJedisPool().getResource(); + public StatefulRedisConnection getRedis() { + return database.getRedis(); } @Override - public JedisPool getJedisPool() { - return database.getJedisPool(); + public Datastore getDatastore() { + return datastore; } @Override @@ -99,11 +109,6 @@ public String getName() { return name; } - @Override - public Jedis getMonitorJedis() { - return database.getMonitorJedis(); - } - @Override public Set getHooks() { return database.getHooks(); diff --git a/src/main/java/com/jonahseguin/payload/database/mongo/PayloadMongoMonitor.java b/src/main/java/com/jonahseguin/payload/database/mongo/PayloadMongoMonitor.java index b70c3c6..681e176 100644 --- a/src/main/java/com/jonahseguin/payload/database/mongo/PayloadMongoMonitor.java +++ b/src/main/java/com/jonahseguin/payload/database/mongo/PayloadMongoMonitor.java @@ -5,7 +5,6 @@ package com.jonahseguin.payload.database.mongo; -import com.jonahseguin.payload.database.DatabaseDependent; import com.jonahseguin.payload.database.PayloadDatabase; import com.mongodb.event.ServerHeartbeatFailedEvent; import com.mongodb.event.ServerHeartbeatStartedEvent; @@ -15,7 +14,6 @@ public class PayloadMongoMonitor implements ServerMonitorListener { private final PayloadDatabase database; - private boolean connected = false; public PayloadMongoMonitor(PayloadDatabase database) { this.database = database; @@ -23,38 +21,31 @@ public PayloadMongoMonitor(PayloadDatabase database) { @Override public void serverHearbeatStarted(ServerHeartbeatStartedEvent event) { - if (!this.connected && !this.database.getState().isMongoInitConnect()) { - // MongoDB connection attempting for first time + if (!this.database.getState().isMongoInitConnect()) { this.database.getErrorService().debug("Attempting MongoDB initial connection"); } } @Override public void serverHeartbeatSucceeded(ServerHeartbeatSucceededEvent event) { - // Connected - this.database.getState().setMongoConnected(true); if (!this.database.getState().isMongoInitConnect()) { - this.database.getHooks().forEach(DatabaseDependent::onMongoDbInitConnect); this.database.getState().setMongoInitConnect(true); this.database.getErrorService().debug("MongoDB initial connection succeeded"); } else { - if (!this.connected) { - this.database.getHooks().forEach(DatabaseDependent::onMongoDbReconnect); - this.database.getErrorService().debug("MongoDB re-connection succeeded"); + if (!database.getState().isMongoConnected()) { + this.database.getErrorService().debug("MongoDB connection restored"); } } - this.connected = true; + this.database.getState().setMongoConnected(true); } @Override public void serverHeartbeatFailed(ServerHeartbeatFailedEvent event) { // Lost connection or failed to connect - this.database.getState().setMongoConnected(false); - if (this.connected) { - this.database.getHooks().forEach(DatabaseDependent::onMongoDbDisconnect); - this.database.getErrorService().debug("MongoDB disconnected"); + if (this.database.getState().isMongoConnected()) { + this.database.getErrorService().capture("MongoDB connection lost"); } - this.connected = false; + this.database.getState().setMongoConnected(false); } } diff --git a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java index 1b61250..86715f4 100644 --- a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java +++ b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java @@ -1,5 +1,11 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + package com.jonahseguin.payload.database.redis; +import io.lettuce.core.RedisURI; import lombok.Data; import org.bukkit.configuration.ConfigurationSection; @@ -39,6 +45,21 @@ public static PayloadRedis fromConfig(ConfigurationSection section) { return new PayloadRedis(address, port, auth, password, ssl, uri, retryTimeout); } + public RedisURI getRedisURI() { + if (useURI()) { + return RedisURI.create(this.uri); + } else { + RedisURI.Builder builder = RedisURI.builder() + .withPort(port) + .withHost(address) + .withSsl(ssl); + if (auth) { + builder.withPassword(password); + } + return builder.build(); + } + } + public boolean useURI() { return this.uri != null; } diff --git a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java index c4352d8..abfe277 100644 --- a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java +++ b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java @@ -6,10 +6,8 @@ package com.jonahseguin.payload.database.redis; import com.jonahseguin.payload.PayloadPlugin; -import com.jonahseguin.payload.database.DatabaseDependent; import com.jonahseguin.payload.database.PayloadDatabase; import org.bukkit.scheduler.BukkitTask; -import redis.clients.jedis.Jedis; public class PayloadRedisMonitor implements Runnable { @@ -48,18 +46,15 @@ public boolean isRunning() { @Override public void run() { // Check if connection is alive - if (database.getJedisPool() == null) return; + if (database.getRedis() == null) return; try { - Jedis jedis = database.getMonitorJedis(); - if (jedis != null) { - if (jedis.isConnected()) { - jedis.ping(); - // Connected - this.handleConnected(); - } else { - // Disconnected - this.handleDisconnected(); - } + String reply = database.getRedis().sync().ping(); + + if (reply.contains("OK")) { + this.handleConnected(); + } else { + this.handleDisconnected(); + database.getErrorService().capture("Non-OK ping reply in Redis Monitor: " + reply); } } catch (Exception ex) { @@ -72,21 +67,19 @@ public void run() { private void handleConnected() { if (!this.database.getState().isRedisConnected()) { this.database.getState().setRedisConnected(true); - if (this.database.getState().isRedisInitConnect()) { - this.database.getHooks().forEach(DatabaseDependent::onRedisReconnect); - } else { + if (!this.database.getState().isRedisInitConnect()) { this.database.getState().setRedisInitConnect(true); - this.database.getHooks().forEach(DatabaseDependent::onRedisInitConnect); + database.getErrorService().debug("Redis initial connection succeeded"); + } else { + database.getErrorService().debug("Redis connection restored"); } - database.getErrorService().debug("Redis connected"); } } private void handleDisconnected() { if (this.database.getState().isRedisConnected()) { this.database.getState().setRedisConnected(false); - this.database.getHooks().forEach(DatabaseDependent::onRedisDisconnect); - database.getErrorService().debug("Redis connection lost"); + database.getErrorService().capture("Redis connection lost"); } this.database.connectRedis(); } diff --git a/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java b/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java deleted file mode 100644 index d4a19b8..0000000 --- a/src/main/java/com/jonahseguin/payload/mode/object/NetworkObject.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.mode.object; - -import com.google.common.base.Preconditions; -import com.google.inject.Inject; -import com.jonahseguin.payload.base.network.NetworkPayload; -import com.jonahseguin.payload.server.ServerService; -import dev.morphia.annotations.Entity; - -import javax.annotation.Nonnull; - -@Entity -public class NetworkObject extends NetworkPayload { - - private String identifier = ""; - - @Inject - public NetworkObject(ServerService serverService) { - super(serverService); - } - - public void markLoaded() { - loaded = true; - lastCached = System.currentTimeMillis(); - mostRecentServer = serverService.getThisServer().getName(); - } - - public void markUnloaded() { - loaded = false; - } - - public void markSaved() { - mostRecentServer = serverService.getThisServer().getName(); - lastSaved = System.currentTimeMillis(); - } - - @Override - public String getIdentifier() { - return identifier; - } - - @Override - public void setIdentifier(@Nonnull String identifier) { - Preconditions.checkNotNull(identifier); - this.identifier = identifier; - } -} diff --git a/src/main/java/com/jonahseguin/payload/mode/object/ObjectCache.java b/src/main/java/com/jonahseguin/payload/mode/object/ObjectCache.java index 2ea6197..3e875d4 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/ObjectCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/ObjectCache.java @@ -9,7 +9,7 @@ import javax.annotation.Nonnull; -public interface ObjectCache extends Cache { +public interface ObjectCache extends Cache { @Override @Nonnull diff --git a/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java b/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java deleted file mode 100644 index 505d88c..0000000 --- a/src/main/java/com/jonahseguin/payload/mode/object/ObjectHandshake.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.mode.object; - -import com.google.common.base.Preconditions; -import com.google.inject.Injector; -import com.jonahseguin.payload.base.handshake.Handshake; -import com.jonahseguin.payload.base.handshake.HandshakeData; - -import javax.annotation.Nonnull; -import java.util.Optional; - -public class ObjectHandshake extends Handshake { - - public static final String KEY_IDENTIFIER = "identifier"; - private final ObjectCache cache; - private String identifier = null; - - public ObjectHandshake(Injector injector, @Nonnull ObjectCache cache) { - super(injector); - Preconditions.checkNotNull(cache); - this.cache = cache; - } - - public ObjectHandshake(Injector injector, @Nonnull ObjectCache cache, @Nonnull String identifier) { - this(injector, cache); - Preconditions.checkNotNull(identifier); - this.identifier = identifier; - } - - @Override - public ObjectHandshake create() { - return new ObjectHandshake(injector, cache); - } - - @Override - public String channelPublish() { - return "payload-object-handshake-" + cache.getName(); - } - - @Override - public String channelReply() { - return "payload-object-handshake-" + cache.getName() + "-reply"; - } - - @Override - public void load(@Nonnull HandshakeData data) { - this.identifier = data.getDocument().getString(KEY_IDENTIFIER); - } - - @Override - public void write(@Nonnull HandshakeData data) { - data.append(KEY_IDENTIFIER, identifier); - } - - @Override - public void receive() { - Optional o = cache.getFromCache(identifier); - if (o.isPresent()) { - PayloadObject object = o.get(); - object.setHandshakeStartTimestamp(System.currentTimeMillis()); - if (!cache.save(object)) { - cache.getErrorService().capture("Failed to save during handshake for object " + object.getIdentifier()); - } - } - } - - @Override - public boolean shouldAccept() { - Optional o = cache.getLocalStore().get(identifier); - if (o.isPresent()) { - PayloadObject object = o.get(); - Optional ono = cache.getNetworked(object); - if (ono.isPresent()) { - NetworkObject no = ono.get(); - return no.isThisMostRelevantServer(); - } - } - return false; - } -} diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java index 675288e..9d43916 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectCache.java @@ -8,7 +8,6 @@ import com.google.common.base.Preconditions; import com.google.inject.Injector; import com.google.inject.Singleton; -import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.PayloadCache; import com.jonahseguin.payload.base.store.PayloadStore; import com.jonahseguin.payload.base.type.PayloadInstantiator; @@ -28,7 +27,7 @@ @Getter @Singleton -public class PayloadObjectCache extends PayloadCache implements ObjectCache { +public class PayloadObjectCache extends PayloadCache implements ObjectCache { private final ObjectCacheSettings settings = new ObjectCacheSettings(); private final ConcurrentMap> controllers = new ConcurrentHashMap<>(); @@ -36,7 +35,7 @@ public class PayloadObjectCache extends PayloadCache mongoStore = new ObjectStoreMongo<>(this); public PayloadObjectCache(Injector injector, PayloadInstantiator instantiator, String name, Class payload) { - super(injector, instantiator, name, String.class, payload, NetworkObject.class); + super(injector, instantiator, name, String.class, payload); setupModule(); } @@ -53,13 +52,9 @@ protected boolean initialize() { if (settings.isUseMongo()) { if (!mongoStore.start()) { success = false; - errorService.capture("Failed to start MongoDB store for cache " + name); + errorService.capture("Failed to start MongoDB store"); } } - /*if (mode.equals(PayloadMode.NETWORK_NODE)) { - handshakeService.subscribe(new ObjectHandshake(injector, this)); - }*/ - database.getMorphia().map(NetworkObject.class); return success; } @@ -68,13 +63,9 @@ protected boolean terminate() { boolean success = true; AtomicInteger failedSaves = new AtomicInteger(0); getCached().forEach(payload -> { - getNetworked(payload).ifPresent(no -> { - if (no.isThisMostRelevantServer()) { - if (!save(payload)) { - failedSaves.getAndIncrement(); - } - } - }); + if (!save(payload)) { + failedSaves.getAndIncrement(); + } }); if (failedSaves.get() > 0) { errorService.capture(failedSaves + " objects failed to save during shutdown"); @@ -99,37 +90,6 @@ public PayloadObjectController controller(@Nonnull String key) { return controllers.computeIfAbsent(key, s -> new PayloadObjectController<>(this, s)); } - @Override - public boolean saveNoSync(@Nonnull X payload) { - boolean success = true; - if (!localStore.save(payload)) { - errorService.capture("Failed to save to local store for object " + payload.getIdentifier()); - success = false; - } - if (!mongoStore.save(payload)) { - errorService.capture("Failed to save to MongoDB store for object " + payload.getIdentifier()); - success = false; - } - /*if (success && mode.equals(PayloadMode.NETWORK_NODE)) { - runAsync(() -> { - Optional o = networkService.get(payload); - if (o.isPresent()) { - NetworkObject no = o.get(); - no.markSaved(); - if (!networkService.save(no)) { - errorService.capture("Failed to save Network Object for object " + payload.getIdentifier()); - } - } - }); - }*/ - return success; - } - - @Override - public NetworkObject createNetworked() { - return injector.getInstance(NetworkObject.class); - } - @Nonnull @Override public PayloadStore getDatabaseStore() { @@ -170,35 +130,9 @@ public Set getAll() { @Override public int saveAll() { AtomicInteger failures = new AtomicInteger(); - if (mode.equals(PayloadMode.NETWORK_NODE)) { - /*for (X object : localStore.getAll()) { - save(object); - getNetworked(object).ifPresent(networkObject -> { - if (networkObject.isThisMostRelevantServer()) { - networkObject.markSaved(); - boolean fail = false; - if (!getNetworkService().save(networkObject)) { - fail = true; - } - if (!save(object)) { - fail = true; - } - if (fail) { - failures.getAndIncrement(); - } - } - }); - }*/ - for (X object : localStore.getAll()) { - if (!save(object)) { - failures.getAndIncrement(); - } - } - } else { - for (X object : localStore.getAll()) { - if (!save(object)) { - failures.getAndIncrement(); - } + for (X object : localStore.getAll()) { + if (!save(object)) { + failures.getAndIncrement(); } } return failures.get(); diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java index 529bf6a..0e61cc0 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java @@ -6,7 +6,6 @@ package com.jonahseguin.payload.mode.object; import com.google.common.base.Preconditions; -import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.type.PayloadController; import lombok.Getter; import lombok.Setter; @@ -97,12 +96,5 @@ public void uncache(@Nonnull X payload, boolean switchingServers) { if (cache.isCached(payload.getIdentifier())) { cache.uncache(payload); } - if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { - Optional o = cache.getNetworkService().get(payload.getIdentifier()); - if (o.isPresent()) { - NetworkObject networkObject = o.get(); - networkObject.markUnloaded(); - } - } } } diff --git a/src/main/java/com/jonahseguin/payload/mode/object/settings/ObjectCacheSettings.java b/src/main/java/com/jonahseguin/payload/mode/object/settings/ObjectCacheSettings.java index aedb486..77ff433 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/settings/ObjectCacheSettings.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/settings/ObjectCacheSettings.java @@ -16,6 +16,5 @@ public class ObjectCacheSettings extends CacheSettings { private boolean useRedis = true; private boolean useMongo = true; private boolean createOnNull = false; - private int handshakeTimeoutSeconds = 5; } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java index 75f35d5..f6ed96b 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java @@ -51,7 +51,7 @@ public abstract class PayloadProfile implements Payload { // Profile has been saved successfully protected transient String loadingSource = null; protected transient Player player = null; - protected transient long handshakeStartTimestamp = 0; + protected transient long handshakeStartTimestamp = 0; // the time when a handshake starts (when another server requests that we save this profile) @Inject public PayloadProfile(ProfileCache cache) { diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index 2d81315..b2bc152 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -9,14 +9,15 @@ import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Singleton; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.PayloadCache; import com.jonahseguin.payload.base.store.PayloadStore; -import com.jonahseguin.payload.base.sync.SyncService; import com.jonahseguin.payload.base.type.PayloadInstantiator; import com.jonahseguin.payload.base.uuid.UUIDService; +import com.jonahseguin.payload.mode.profile.handshake.ProfileHandshakeService; +import com.jonahseguin.payload.mode.profile.network.NetworkProfile; +import com.jonahseguin.payload.mode.profile.network.NetworkService; +import com.jonahseguin.payload.mode.profile.network.RedisNetworkService; import com.jonahseguin.payload.mode.profile.settings.ProfileCacheSettings; import com.jonahseguin.payload.mode.profile.store.ProfileStoreLocal; import com.jonahseguin.payload.mode.profile.store.ProfileStoreMongo; @@ -30,24 +31,24 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Getter @Singleton -public class PayloadProfileCache extends PayloadCache implements LangModule, ProfileCache { +public class PayloadProfileCache extends PayloadCache implements ProfileCache { private final ProfileCacheSettings settings = new ProfileCacheSettings(); private final ConcurrentMap> controllers = new ConcurrentHashMap<>(); private final ProfileStoreLocal localStore = new ProfileStoreLocal<>(this); private final ProfileStoreMongo mongoStore = new ProfileStoreMongo<>(this); @Inject private UUIDService uuidService; + private NetworkService networkService = null; + private ProfileHandshakeService handshakeService = null; public PayloadProfileCache(Injector injector, PayloadInstantiator instantiator, String name, Class payload) { - super(injector, instantiator, name, UUID.class, payload, NetworkProfile.class); + super(injector, instantiator, name, UUID.class, payload); this.setupModule(); - lang.register(this); } @Override @@ -55,18 +56,8 @@ protected void setupModule() { super.injectMe(); super.setupModule(); injector.injectMembers(this); - } - - @Override - public void define(LangDefinitions l) { - l.define("deny-join-database", "&cThe database is currently offline. We are working on resolving this issue as soon as possible, please try again soon."); - l.define("no-profile", "&cYour profile is not loaded. Please wait as we will continue to attempt to load it."); - l.define("shutdown", "&cThe server has shutdown."); - } - - @Override - public String langModule() { - return "profile-cache"; + networkService = new RedisNetworkService<>(this); + handshakeService = new ProfileHandshakeService<>(this, database); } @Override @@ -74,14 +65,17 @@ protected boolean initialize() { boolean success = true; if (!localStore.start()) { success = false; - errorService.capture("Failed to start Local store for cache " + name); + errorService.capture("Failed to start Local store for cache: " + name); } if (!mongoStore.start()) { success = false; - errorService.capture("Failed to start MongoDB store for cache " + name); + errorService.capture("Failed to start MongoDB store for cache: " + name); } if (mode.equals(PayloadMode.NETWORK_NODE)) { - handshakeService.subscribe(new ProfileHandshake(injector, this)); + if (!networkService.start()) { + success = false; + errorService.capture("Failed to start Network Service (Network Node mode) for cache: " + name); + } } database.getMorphia().map(NetworkProfile.class); return success; @@ -108,8 +102,14 @@ protected boolean terminate() { if (!mongoStore.shutdown()) { success = false; } - - + if (mode.equals(PayloadMode.NETWORK_NODE)) { + if (networkService.isRunning()) { + if (!networkService.shutdown()) { + success = false; + errorService.capture("Failed to start Network Service (Network Node mode) for cache: " + name); + } + } + } return success; } @@ -125,24 +125,6 @@ public Optional getNetworked(@Nonnull X payload) { return networkService.get(payload); } - @Override - public Future> getAsync(@Nonnull UUID key) { - Preconditions.checkNotNull(key); - return runAsync(() -> get(key)); - } - - @Override - public Future> getAsync(@Nonnull String username) { - Preconditions.checkNotNull(username); - return runAsync(() -> get(username)); - } - - @Override - public Future> getAsync(@Nonnull Player player) { - Preconditions.checkNotNull(player); - return runAsync(() -> get(player)); - } - @Override public void uncache(@Nonnull UUID key) { Preconditions.checkNotNull(key); @@ -168,12 +150,6 @@ public Collection getCached() { return localStore.getAll(); } - @Nonnull - @Override - public SyncService getSyncService() { - return sync; - } - @Nonnull @Override public PayloadStore getDatabaseStore() { @@ -340,52 +316,52 @@ public PayloadProfileController controller(@Nonnull UUID key) { } @Override - public Future saveAsync(@Nonnull X payload) { + public void saveAsync(@Nonnull X payload) { Preconditions.checkNotNull(payload); this.cache(payload); - return this.runAsync(() -> this.save(payload)); + this.runAsync(() -> this.save(payload)); } @Override public boolean save(@Nonnull X payload) { - Preconditions.checkNotNull(payload); - Optional onp = networkService.get(payload); - if (onp.isPresent()) { - NetworkProfile np = onp.get(); - if (this.saveNoSync(payload)) { - np.markSaved(); - if (networkService.save(np)) { - if (settings.isEnableSync()) { - sync.update(payload.getIdentifier()); + Preconditions.checkNotNull(payload, "Cannot save a null Payload"); + cache(payload); + if (mode.equals(PayloadMode.NETWORK_NODE)) { + Optional onp = networkService.get(payload); + if (onp.isPresent()) { + NetworkProfile np = onp.get(); + if (saveMongo(payload)) { + np.markSaved(); + if (networkService.save(np)) { + return true; + } else { + errorService.capture("Failed to save profile " + payload.getName() + ": Couldn't save network profile (but saved normal profile)"); + return false; } - return true; } else { - errorService.capture("Failed to save profile " + payload.getName() + ": Couldn't save network profile (but saved normal profile)"); + errorService.capture("Failed to save profile " + payload.getName() + ": Failed to save to database (via saveMongo())"); return false; } } else { - errorService.capture("Failed to save profile " + payload.getName() + ": Failed to save to database (via saveNoSync())"); + errorService.capture("Failed to save profile " + payload.getName() + ": Network Profile doesn't exist (should have been created)"); return false; } } else { - errorService.capture("Failed to save profile " + payload.getName() + ": Network Profile doesn't exist"); - return false; + return saveMongo(payload); } } - @Override - public boolean saveNoSync(@Nonnull X payload) { - Preconditions.checkNotNull(payload); - cache(payload); - if (mongoStore.save(payload)) { + private boolean saveMongo(@Nonnull X payload) { + Preconditions.checkNotNull(payload, "Cannot save a null Payload (saveMongo)"); + boolean mongo = mongoStore.save(payload); + if (mongo) { payload.setSaveFailed(false); payload.setLastSaveTimestamp(System.currentTimeMillis()); payload.interact(); - return true; } else { payload.setSaveFailed(true); - return false; } + return mongo; } @Override @@ -443,7 +419,6 @@ public void updatePayloadID() { } }); } - this.pool.submit(this::saveAll); } @Override diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index 72ff306..c6b9174 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -7,8 +7,9 @@ import com.google.common.base.Preconditions; import com.jonahseguin.payload.PayloadMode; -import com.jonahseguin.payload.base.handshake.HandshakeHandler; +import com.jonahseguin.payload.base.lang.PLang; import com.jonahseguin.payload.base.type.PayloadController; +import com.jonahseguin.payload.mode.profile.network.NetworkProfile; import com.jonahseguin.payload.server.PayloadServer; import lombok.Getter; import lombok.Setter; @@ -35,6 +36,10 @@ public class PayloadProfileController implements Paylo private boolean failure = false; private int timeoutAttempts = 0; + private long handshakeRequestStartTime = 0L; + private boolean handshakeTimedOut = false; + private boolean handshakeComplete = false; + PayloadProfileController(@Nonnull PayloadProfileCache cache, @Nonnull UUID uuid) { Preconditions.checkNotNull(cache); Preconditions.checkNotNull(uuid); @@ -46,6 +51,10 @@ public void reset() { denyJoin = false; payload = null; failure = false; + + handshakeComplete = false; + handshakeRequestStartTime = 0L; + handshakeTimedOut = false; } public void login(@Nonnull String username, @Nonnull String loginIp) { @@ -66,7 +75,7 @@ public Optional cache() { if (login) { if (!cache.getDatabase().getState().canCacheFunction(cache)) { denyJoin = true; - joinDenyReason = cache.getLang().module(cache).format("deny-join-database", cache.getName()); + joinDenyReason = cache.getLang().format(PLang.JOIN_DENY_DATABASE, cache.getName()); return Optional.empty(); } } @@ -123,7 +132,7 @@ private Optional cacheStandalone() { payload.setUUID(uuid); payload.setLoginIp(loginIp); payload.setLoadingSource("New Profile"); - cache.getPool().submit(() -> cache.save(payload)); + cache.saveAsync(payload); } // If they aren't logging in (getting a payload by UUID/username) and it wasn't found, return null as they don't exist. } else { @@ -213,9 +222,8 @@ private Optional cacheNetworkNode() { if (server != null && server.isOnline()) { // Handshake cache.getErrorService().debug("Handshaking " + uuid.toString() + " from server " + server.getName()); - HandshakeHandler handshake = cache.getHandshakeService().publish(new ProfileHandshake(cache.getInjector(), cache, uuid, server.getName())); - Optional o = handshake.waitForReply(cache.getSettings().getHandshakeTimeoutSeconds()); - if (o.isPresent()) { + handshake(server); + if (handshakeComplete && !handshakeTimedOut) { timeoutAttempts = 0; cache.getErrorService().debug("Handshake complete for " + uuid.toString() + ", loading from DB"); load(false); @@ -234,7 +242,7 @@ private Optional cacheNetworkNode() { } } else { // Target server isn't online, or there is no recent server - cache.getErrorService().debug("Target server '" + (server != null ? server.getName() : "n/a") + "' not online for handshake for " + uuid.toString(), ", loading from database"); + cache.getErrorService().debug("Target server '" + (server != null ? server.getName() : "n/a") + "' not online for handshake for " + uuid.toString() + ", loading from database"); load(false); } } else { @@ -288,4 +296,19 @@ public void initializeOnJoin(Player player) { } } + private void handshake(@Nonnull PayloadServer targetServer) { + cache.getHandshakeService().handshake(this, targetServer); + while (!handshakeComplete && ((System.currentTimeMillis() - handshakeRequestStartTime) / 1000) < cache.getSettings().getHandshakeTimeoutSeconds()) { + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + cache.getErrorService().capture(ex, "Interrupted in PayloadProfileController while waiting for handshake for UUID: " + uuid.toString()); + break; + } + } + if (!handshakeComplete) { + handshakeTimedOut = true; + } + } + } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java index ae18eac..73247c9 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java @@ -6,6 +6,8 @@ package com.jonahseguin.payload.mode.profile; import com.jonahseguin.payload.base.Cache; +import com.jonahseguin.payload.mode.profile.network.NetworkProfile; +import com.jonahseguin.payload.mode.profile.network.NetworkService; import com.jonahseguin.payload.mode.profile.settings.ProfileCacheSettings; import org.bukkit.entity.Player; @@ -13,17 +15,16 @@ import java.util.Collection; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.Future; -public interface ProfileCache extends Cache { +public interface ProfileCache extends Cache { - Optional get(@Nonnull String username); + Optional getNetworked(@Nonnull UUID key); - Optional get(@Nonnull Player player); + Optional getNetworked(@Nonnull X payload); - Future> getAsync(@Nonnull String username); + Optional get(@Nonnull String username); - Future> getAsync(@Nonnull Player player); + Optional get(@Nonnull Player player); Optional getFromCache(@Nonnull String username); @@ -47,4 +48,8 @@ public interface ProfileCache extends Cache controller(@Nonnull UUID key); + NetworkService getNetworkService(); + + NetworkProfile createNetworked(); + } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java deleted file mode 100644 index 56b35e1..0000000 --- a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileHandshake.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.mode.profile; - -import com.google.common.base.Preconditions; -import com.google.inject.Injector; -import com.jonahseguin.payload.base.handshake.Handshake; -import com.jonahseguin.payload.base.handshake.HandshakeData; - -import javax.annotation.Nonnull; -import java.util.Optional; -import java.util.UUID; - -public class ProfileHandshake extends Handshake { - - public static final String KEY_UUID = "uuid"; - public static final String KEY_SERVER = "targetServer"; - private final ProfileCache cache; - private UUID uuid = null; - private String targetServer = null; - - public ProfileHandshake(Injector injector, @Nonnull ProfileCache cache) { - super(injector); - Preconditions.checkNotNull(cache); - this.cache = cache; - } - - public ProfileHandshake(Injector injector, @Nonnull ProfileCache cache, @Nonnull UUID uuid, @Nonnull String targetServer) { - this(injector, cache); - Preconditions.checkNotNull(uuid); - this.uuid = uuid; - this.targetServer = targetServer; - } - - @Override - public ProfileHandshake create() { - return new ProfileHandshake(injector, cache); - } - - @Override - public String channelPublish() { - return "payload-profile-handshake-" + cache.getName(); - } - - @Override - public String channelReply() { - return "payload-profile-handshake-" + cache.getName() + "-reply"; - } - - @Override - public void load(@Nonnull HandshakeData data) { - try { - this.uuid = UUID.fromString(data.getDocument().getString(KEY_UUID)); - this.targetServer = data.getDocument().getString(KEY_SERVER); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - @Override - public void write(@Nonnull HandshakeData data) { - try { - data.append(KEY_UUID, this.uuid.toString()); - data.append(KEY_SERVER, this.targetServer); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - @Override - public void receive() { - Optional o = cache.getFromCache(uuid); - if (o.isPresent()) { - PayloadProfile profile = o.get(); - profile.setHandshakeStartTimestamp(System.currentTimeMillis()); - if (!cache.save(profile)) { - cache.getErrorService().capture("Failed to save during handshake for " + profile.getName()); - } - } else { - // We don't have them but they tried to handshake from here - cache.getErrorService().debug("Skipping saving for Profile Handshake (not cached), replying: " + uuid.toString()); - } - } - - @Override - public boolean shouldReply() { - return true; - } - - @Override - public boolean shouldAccept() { - return targetServer.equalsIgnoreCase(cache.getDatabase().getServerService().getThisServer().getName()); - } -} diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java new file mode 100644 index 0000000..628fd3c --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.mode.profile.handshake; + +import com.google.common.base.Preconditions; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.bson.Document; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.UUID; + +@Data +@AllArgsConstructor +@RequiredArgsConstructor +public class ProfileHandshakePacket { + + private static final String KEY_SENDER_SERVER = "senderServer"; + private static final String KEY_TARGET_SERVER = "targetServer"; + private static final String KEY_UUID = "uuid"; + + private String senderServer; + private final UUID uuid; + private String targetServer; + + @Nullable + public static ProfileHandshakePacket fromJSON(@Nonnull String json) { + Preconditions.checkNotNull(json, "JSON cannot be null for ProfileHandshakePacket"); + Document document = Document.parse(json); + String uuidString = document.getString(KEY_UUID); + String targetServer = document.getString(KEY_TARGET_SERVER); + String senderServer = document.getString(KEY_SENDER_SERVER); + + if (uuidString != null && targetServer != null && senderServer != null) { + return new ProfileHandshakePacket(senderServer, UUID.fromString(uuidString), targetServer); + } + return null; + } + + public Document toDocument() { + Document document = new Document(); + document.append(KEY_TARGET_SERVER, targetServer); + document.append(KEY_UUID, uuid); + document.append(KEY_SENDER_SERVER, senderServer); + return document; + } + +} diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java new file mode 100644 index 0000000..fe5f046 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.mode.profile.handshake; + +import com.google.common.base.Preconditions; +import com.google.inject.Inject; +import com.jonahseguin.payload.base.Service; +import com.jonahseguin.payload.database.DatabaseService; +import com.jonahseguin.payload.mode.profile.PayloadProfile; +import com.jonahseguin.payload.mode.profile.PayloadProfileController; +import com.jonahseguin.payload.mode.profile.ProfileCache; +import com.jonahseguin.payload.server.PayloadServer; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; +import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; +import org.bukkit.entity.Player; + +import javax.annotation.Nonnull; +import java.util.UUID; + +public class ProfileHandshakeService implements Service { + + private final ProfileCache cache; + private final DatabaseService database; + private final String channelRequest; + private final String channelReply; + private boolean running = false; + private RedisPubSubReactiveCommands reactive = null; + + @Inject + public ProfileHandshakeService(@Nonnull ProfileCache cache, @Nonnull DatabaseService database) { + Preconditions.checkNotNull(cache, "Cache cannot be null"); + Preconditions.checkNotNull(database, "Database cannot be null"); + Preconditions.checkNotNull(cache.getName(), "Cache name cannot be null"); + this.cache = cache; + this.database = database; + this.channelRequest = "payload-profile-handshake-" + cache.getName() + "-request"; + this.channelReply = "payload-profile-handshake-" + cache.getName() + "-reply"; + } + + @Override + public boolean start() { + Preconditions.checkState(!running, "Profile Handshake Service is already running!"); + boolean sub = subscribe(); + running = true; + return sub; + } + + @Override + public boolean shutdown() { + Preconditions.checkState(running, "Profile Handshake Service is not running!"); + if (reactive != null) { + reactive.unsubscribe(channelRequest, channelReply); + } + running = false; + return false; + } + + @Override + public boolean isRunning() { + return running; + } + + private boolean subscribe() { + try { + StatefulRedisPubSubConnection connection = database.getRedisPubSub(); + reactive = connection.reactive(); + + reactive.subscribe(channelRequest, channelReply).subscribe(); + + reactive.observeChannels() + .filter(pm -> pm.getChannel().equals(channelRequest) || pm.getChannel().equals(channelReply)) + .doOnNext(patternMessage -> { + ProfileHandshakePacket packet = ProfileHandshakePacket.fromJSON(patternMessage.getMessage()); + if (packet != null) { + if (packet.getTargetServer().equalsIgnoreCase(database.getServerService().getThisServer().getName())) { + if (patternMessage.getChannel().equals(channelRequest)) { + handleRequest(packet); + } else if (patternMessage.getChannel().equals(channelRequest)) { + handleReply(packet); + } + } + } + }).subscribe(); + + return true; + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error subscribing in Profile Handshake Service for channels: " + channelRequest + ", " + channelReply); + return false; + } + } + + private void sendReply(@Nonnull ProfileHandshakePacket requestPacket) { + Preconditions.checkNotNull(requestPacket, "RequestPacket cannot be null in ProfileHandshakeService (in sendReply)"); + requestPacket.setTargetServer(requestPacket.getSenderServer()); + requestPacket.setSenderServer(database.getServerService().getThisServer().getName()); + String json = requestPacket.toDocument().toJson(); + Preconditions.checkNotNull(json, "JSON cannot be null for sendReply in ProfileHandshakeService"); + database.getRedisPubSub().async().publish(channelReply, json); + } + + private void handleRequest(@Nonnull final ProfileHandshakePacket packet) { + Preconditions.checkNotNull(packet, "ProfileHandshakePacket cannot be null"); + // Another server is being joined by the player (who is assumingly on this server) + // - check if they're online, if they are: save them + // - after save (or if they're not online) send reply + final Player player = cache.getPlugin().getServer().getPlayer(packet.getUuid()); + cache.runAsync(() -> { + if (player != null && player.isOnline()) { + cache.getFromCache(player).ifPresent(cache::save); + } + sendReply(packet); + }); + } + + private void handleReply(@Nonnull ProfileHandshakePacket packet) { + Preconditions.checkNotNull(packet, "ProfileHandshakePacket cannot be null"); + UUID uuid = packet.getUuid(); + if (uuid != null) { + PayloadProfileController controller = cache.controller(uuid); + controller.setHandshakeComplete(true); + controller.setHandshakeTimedOut(false); + } + } + + public void handshake(@Nonnull PayloadProfileController controller, PayloadServer targetServer) { + ProfileHandshakePacket packet = new ProfileHandshakePacket(database.getServerService().getThisServer().getName(), controller.getUuid(), targetServer.getName()); + String json = packet.toDocument().toJson(); + Preconditions.checkNotNull(json, "JSON cannot be null for handshake in ProfileHandshakeService"); + controller.setHandshakeTimedOut(false); + controller.setHandshakeComplete(false); + controller.setHandshakeRequestStartTime(System.currentTimeMillis()); + database.getRedisPubSub().async().publish(channelRequest, json); + } + +} diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index 6607e4b..99d26c2 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -9,9 +9,13 @@ import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.Cache; -import com.jonahseguin.payload.mode.profile.*; +import com.jonahseguin.payload.mode.profile.PayloadProfile; +import com.jonahseguin.payload.mode.profile.PayloadProfileCache; +import com.jonahseguin.payload.mode.profile.PayloadProfileController; +import com.jonahseguin.payload.mode.profile.ProfileCache; import com.jonahseguin.payload.mode.profile.event.PayloadProfileLogoutEvent; import com.jonahseguin.payload.mode.profile.event.PayloadProfileSwitchServersEvent; +import com.jonahseguin.payload.mode.profile.network.NetworkProfile; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java b/src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkProfile.java similarity index 81% rename from src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java rename to src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkProfile.java index b92e155..e0c1402 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/NetworkProfile.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkProfile.java @@ -3,23 +3,33 @@ * www.jonahseguin.com */ -package com.jonahseguin.payload.mode.profile; +package com.jonahseguin.payload.mode.profile.network; import com.google.common.base.Preconditions; import com.google.inject.Inject; -import com.jonahseguin.payload.base.network.NetworkPayload; import com.jonahseguin.payload.server.ServerService; import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import dev.morphia.annotations.Transient; import lombok.Getter; import lombok.Setter; +import org.bson.types.ObjectId; import javax.annotation.Nonnull; import java.util.UUID; -@Entity +@Entity("NetworkProfile") @Getter @Setter -public class NetworkProfile extends NetworkPayload { +public class NetworkProfile { + + @Id + private ObjectId id = new ObjectId(); // required for morphia mapping + + @Transient + protected transient final ServerService serverService; + protected long lastCached = System.currentTimeMillis(); + protected long lastSaved = System.currentTimeMillis(); protected String identifier; protected String lastSeenServer; @@ -29,7 +39,7 @@ public class NetworkProfile extends NetworkPayload { @Inject public NetworkProfile(ServerService serverService) { - super(serverService); + this.serverService = serverService; } public boolean isOnlineOtherServer() { @@ -56,10 +66,8 @@ public boolean isOnline() { public void markLoaded(boolean online) { this.online = online; - loaded = true; lastCached = System.currentTimeMillis(); if (online) { - mostRecentServer = serverService.getThisServer().getName(); lastSeenServer = serverService.getThisServer().getName(); lastSeen = System.currentTimeMillis(); } @@ -67,7 +75,6 @@ public void markLoaded(boolean online) { public void markUnloaded(boolean switchingServers) { online = switchingServers; - loaded = switchingServers; lastSeen = System.currentTimeMillis(); } @@ -78,13 +85,11 @@ public void markSaved() { } } - @Override public UUID getIdentifier() { setUUID(); return uuidID; } - @Override public void setIdentifier(@Nonnull UUID identifier) { Preconditions.checkNotNull(identifier); this.identifier = identifier.toString(); diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkService.java b/src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkService.java new file mode 100644 index 0000000..8e2e950 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/mode/profile/network/NetworkService.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.mode.profile.network; + +import com.jonahseguin.payload.base.Service; +import com.jonahseguin.payload.mode.profile.PayloadProfile; + +import javax.annotation.Nonnull; +import java.util.Optional; +import java.util.UUID; + +public interface NetworkService extends Service { + + Optional get(@Nonnull UUID key); + + Optional get(@Nonnull X payload); + + boolean has(@Nonnull UUID key); + + boolean save(@Nonnull NetworkProfile networkProfile); + + Optional get(@Nonnull NetworkProfile payload); + + NetworkProfile create(@Nonnull X payload); + +} diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/network/RedisNetworkService.java b/src/main/java/com/jonahseguin/payload/mode/profile/network/RedisNetworkService.java new file mode 100644 index 0000000..d0178f5 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/mode/profile/network/RedisNetworkService.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.mode.profile.network; + +import com.google.common.base.Preconditions; +import com.jonahseguin.payload.database.DatabaseService; +import com.jonahseguin.payload.mode.profile.PayloadProfile; +import com.jonahseguin.payload.mode.profile.ProfileCache; +import com.mongodb.BasicDBObject; + +import javax.annotation.Nonnull; +import java.util.Optional; +import java.util.UUID; + +public class RedisNetworkService implements NetworkService { + + private final ProfileCache cache; + private final DatabaseService database; + private boolean running = false; + + public RedisNetworkService(ProfileCache cache) { + this.cache = cache; + this.database = cache.getDatabase(); + } + + @Override + public Optional get(@Nonnull UUID uuid) { + Preconditions.checkNotNull(uuid, "UUID cannot be null"); + final String hashKey = cache.getServerSpecificName(); + final String keyString = cache.keyToString(uuid); + Preconditions.checkNotNull(hashKey, "Hash key cannot be null"); + Preconditions.checkNotNull(keyString, "Key cannot be null"); + try { + if (database.getRedis().sync().hexists(hashKey, keyString)) { + final String json = database.getRedis().sync().hget(hashKey, keyString); + if (json != null && json.length() > 0) { + BasicDBObject dbObject = BasicDBObject.parse(json); + NetworkProfile networkProfile = database.getMorphia().fromDBObject(database.getDatastore(), NetworkProfile.class, dbObject); + return Optional.ofNullable(networkProfile); + } else { + return Optional.empty(); + } + } else { + return Optional.empty(); + } + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error getting network payload from Key in Redis Network Service"); + return Optional.empty(); + } + } + + @Override + public Optional get(@Nonnull X payload) { + Preconditions.checkNotNull(payload, "Payload cannot be null"); + final String hashKey = cache.getServerSpecificName(); + final String keyString = cache.keyToString(payload.getIdentifier()); + Preconditions.checkNotNull(hashKey, "Hash key cannot be null"); + Preconditions.checkNotNull(keyString, "Key cannot be null"); + try { + if (database.getRedis().sync().hexists(hashKey, keyString)) { + final String json = database.getRedis().sync().hget(hashKey, keyString); + if (json != null && json.length() > 0) { + BasicDBObject dbObject = BasicDBObject.parse(json); + NetworkProfile networkProfile = database.getMorphia().fromDBObject(database.getDatastore(), NetworkProfile.class, dbObject); + if (networkProfile != null) { + networkProfile.setIdentifier(payload.getIdentifier()); + return Optional.of(networkProfile); + } else { + return createForGet(payload); + } + } else { + return createForGet(payload); + } + } else { + return createForGet(payload); + } + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error getting network payload from Payload in Redis Network Service"); + return Optional.empty(); + } + } + + private Optional createForGet(@Nonnull X payload) { + NetworkProfile networkProfile = create(payload); + if (!save(networkProfile)) { + cache.getErrorService().capture("Failed to save after creation of network payload " + cache.keyToString(payload.getIdentifier())); + } + return Optional.of(networkProfile); + } + + @Override + public boolean has(@Nonnull UUID uuid) { + Preconditions.checkNotNull(uuid, "UUID cannot be null"); + final String hashKey = cache.getServerSpecificName(); + final String keyString = cache.keyToString(uuid); + Preconditions.checkNotNull(hashKey, "Hash key cannot be null"); + Preconditions.checkNotNull(keyString, "Key cannot be null"); + try { + return database.getRedis().sync().hexists(hashKey, keyString); + } catch (Exception ex) { + cache.getErrorService().capture("Error checking if hexists in Redis Network Service for UUID:" + keyString); + } + return false; + } + + @Override + public boolean save(@Nonnull NetworkProfile networkProfile) { + Preconditions.checkNotNull(networkProfile, "NetworkProfile cannot be null"); + final String hashKey = cache.getServerSpecificName(); + final String keyString = cache.keyToString(networkProfile.getIdentifier()); + Preconditions.checkNotNull(hashKey, "Hash key cannot be null"); + Preconditions.checkNotNull(keyString, "Key cannot be null"); + BasicDBObject object = (BasicDBObject) database.getMorphia().toDBObject(networkProfile); + if (object != null) { + String json = object.toJson(); + Preconditions.checkNotNull(json, "JSON cannot be null"); + Preconditions.checkNotNull(cache.getServerSpecificName(), "Server specific cache name cannot be null"); + Preconditions.checkNotNull(networkProfile.getIdentifier(), "Payload identifier cannot be null"); + Preconditions.checkNotNull(cache.keyToString(networkProfile.getIdentifier()), "Payload identifier key cannot be null"); + try { + database.getRedis().async().hset(hashKey, keyString, json); + return true; + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error saving NetworkProfile in Redis Network Service for UUID: " + cache.keyToString(networkProfile.getIdentifier())); + return false; + } + } + return false; + } + + @Override + public Optional get(@Nonnull NetworkProfile payload) { + Preconditions.checkNotNull(payload); + return cache.get(payload.getIdentifier()); + } + + @Override + public NetworkProfile create(@Nonnull X payload) { + Preconditions.checkNotNull(payload); + NetworkProfile np = cache.createNetworked(); + np.setIdentifier(payload.getIdentifier()); + return np; + } + + @Override + public boolean start() { + running = true; + return true; + } + + @Override + public boolean shutdown() { + running = false; + return true; + } + + @Override + public boolean isRunning() { + return running; + } +} diff --git a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java index 6f28f6d..161e5e5 100644 --- a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java +++ b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java @@ -8,41 +8,45 @@ import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.PayloadPlugin; import com.jonahseguin.payload.annotation.Database; import com.jonahseguin.payload.base.error.ErrorService; import com.jonahseguin.payload.database.DatabaseService; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; +import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; import lombok.Getter; +import org.bson.Document; import org.bukkit.scheduler.BukkitTask; -import redis.clients.jedis.Jedis; import javax.annotation.Nonnull; import java.util.Collection; import java.util.Optional; -import java.util.concurrent.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; @Getter @Singleton public class PayloadServerService implements Runnable, ServerService { public static final long ASSUME_OFFLINE_SECONDS = 60; - public static final long PING_FREQUENCY_SECONDS = 10; + public static final long PING_FREQUENCY_SECONDS = 20; + private final PayloadAPI api; private final String name; private final PayloadPlugin payloadPlugin; private final DatabaseService database; private final PayloadServer thisServer; private final ErrorService error; private final ConcurrentMap servers = new ConcurrentHashMap<>(); - private final ExecutorService executorService = Executors.newCachedThreadPool(); - private Jedis jedisSubscriber = null; private ServerPublisher publisher = null; - private ServerSubscriber subscriber = null; private BukkitTask pingTask = null; + private RedisPubSubReactiveCommands reactive = null; private boolean running = false; @Inject - public PayloadServerService(DatabaseService database, PayloadPlugin payloadPlugin, @Database ErrorService error, @Database String name) { + public PayloadServerService(PayloadAPI api, DatabaseService database, PayloadPlugin payloadPlugin, @Database ErrorService error, @Database String name) { + this.api = api; this.name = name; this.database = database; this.payloadPlugin = payloadPlugin; @@ -56,30 +60,56 @@ public boolean start() { try { this.publisher = new ServerPublisher(this); - payloadPlugin.getServer().getScheduler().runTaskAsynchronously(payloadPlugin, () -> { - this.jedisSubscriber = database.getJedisResource(); - this.subscriber = new ServerSubscriber(this); - this.jedisSubscriber.subscribe(this.subscriber, - "server-join", "server-ping", "server-quit", "server-update-name"); - }); - - this.executorService.submit(() -> { - this.jedisSubscriber = database.getJedisResource(); - this.subscriber = new ServerSubscriber(this); - this.jedisSubscriber.subscribe(this.subscriber, - "server-join", "server-ping", "server-quit", "server-update-name"); - }); + boolean sub = subscribe(); this.publisher.publishJoin(); this.pingTask = payloadPlugin.getServer().getScheduler().runTaskTimerAsynchronously(payloadPlugin, this, (PING_FREQUENCY_SECONDS * 20), (PING_FREQUENCY_SECONDS * 20)); running = true; - return true; + return sub; } catch (Exception ex) { error.capture(ex, "Error starting Server Service for database: " + name); return false; } } + private boolean subscribe() { + try { + StatefulRedisPubSubConnection connection = database.getRedisPubSub(); + reactive = connection.reactive(); + + reactive.subscribe("payload-server-join", "payload-server-ping", "payload-server-quit", "payload-server-update-name").subscribe(); + + reactive.observeChannels() + .filter(pm -> !pm.getMessage().equalsIgnoreCase(database.getServerService().getThisServer().getName())) + .filter(pm -> pm.getChannel().equalsIgnoreCase("payload-server-join") || + pm.getChannel().equalsIgnoreCase("payload-server-ping") || + pm.getChannel().equalsIgnoreCase("payload-server-quit") || + pm.getChannel().equalsIgnoreCase("payload-server-update-name")) + .doOnNext(patternMessage -> { + ServerEvent event = ServerEvent.fromChannel(patternMessage.getChannel()); + if (event != null) { + if (event.equals(ServerEvent.JOIN)) { + handleJoin(patternMessage.getMessage()); + } else if (event.equals(ServerEvent.QUIT)) { + handleQuit(patternMessage.getMessage()); + } else if (event.equals(ServerEvent.PING)) { + handlePing(patternMessage.getMessage()); + } else if (event.equals(ServerEvent.UPDATE_NAME)) { + Document data = Document.parse(patternMessage.getMessage()); + String oldName = data.getString("old"); + String newName = data.getString("new"); + handleUpdateName(oldName, newName); + } + } + }).subscribe(); + + return true; + } catch (Exception ex) { + database.getErrorService().capture(ex, "Error subscribing in Payload Server Service"); + return false; + } + } + @Override @Nonnull public PayloadServer register(@Nonnull String name, boolean online) { @@ -136,38 +166,18 @@ private void doPing() { this.publisher.publishPing(); } - private void shutdownExecutor() { - try { - this.executorService.shutdown(); - this.executorService.awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException ex) { - this.error.capture(ex, "Interrupted during shutdown of Server Manager's Executor Service"); - } finally { - this.executorService.shutdownNow(); - } - } - @Override public boolean shutdown() { try { if (this.pingTask != null) { this.pingTask.cancel(); } - this.shutdownExecutor(); - if (this.subscriber != null) { - if (this.subscriber.isSubscribed()) { - this.subscriber.unsubscribe(); - } - this.subscriber = null; + if (reactive != null) { + reactive.unsubscribe("payload-server-join", "payload-server-ping", "payload-server-quit", "payload-server-update-name"); } this.publisher.publishQuit(); // Sync. - this.publisher = null; - if (this.jedisSubscriber != null) { - this.jedisSubscriber.close(); - this.jedisSubscriber = null; - } running = false; return true; } catch (Exception ex) { diff --git a/src/main/java/com/jonahseguin/payload/server/ServerEvent.java b/src/main/java/com/jonahseguin/payload/server/ServerEvent.java index 02532c2..2ad7943 100644 --- a/src/main/java/com/jonahseguin/payload/server/ServerEvent.java +++ b/src/main/java/com/jonahseguin/payload/server/ServerEvent.java @@ -7,10 +7,10 @@ public enum ServerEvent { - JOIN("server-join"), - PING("server-ping"), - QUIT("server-quit"), - UPDATE_NAME("server-update-name"); + JOIN("payload-server-join"), + PING("payload-server-ping"), + QUIT("payload-server-quit"), + UPDATE_NAME("payload-server-update-name"); private final String event; diff --git a/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java b/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java index 1ceac65..25f69c1 100644 --- a/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java +++ b/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java @@ -6,7 +6,6 @@ package com.jonahseguin.payload.server; import org.bson.Document; -import redis.clients.jedis.Jedis; public class ServerPublisher { @@ -17,49 +16,44 @@ public ServerPublisher(PayloadServerService serverService) { } public void publishPing() { - this.payloadServerService.getExecutorService().submit(() -> { - try (Jedis jedis = this.payloadServerService.getDatabase().getJedisResource()) { - jedis.publish(ServerEvent.PING.getEvent(), payloadServerService.getThisServer().getName()); - } - catch (Exception ex) { - payloadServerService.getDatabase().getErrorService().capture(ex, "Server Manager: Error publishing PING event"); + this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { + try { + payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.PING.getEvent(), payloadServerService.getThisServer().getName()); + } catch (Exception ex) { + payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing PING event"); } }); } public void publishJoin() { - this.payloadServerService.getExecutorService().submit(() -> { - try (Jedis jedis = this.payloadServerService.getDatabase().getJedisResource()) { - jedis.publish(ServerEvent.JOIN.getEvent(), payloadServerService.getThisServer().getName()); - } - catch (Exception ex) { - payloadServerService.getDatabase().getErrorService().capture(ex, "Server Manager: Error publishing JOIN event"); + this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { + try { + payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.JOIN.getEvent(), payloadServerService.getThisServer().getName()); + } catch (Exception ex) { + payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing JOIN event"); } }); } public void publishQuit() { - // Sync -- we want this to complete first before shutdown - this.payloadServerService.getExecutorService().submit(() -> { - try (Jedis jedis = this.payloadServerService.getDatabase().getJedisResource()) { - jedis.publish(ServerEvent.QUIT.getEvent(), payloadServerService.getThisServer().getName()); - } - catch (Exception ex) { - payloadServerService.getDatabase().getErrorService().capture(ex, "Server Manager: Error publishing QUIT event"); + this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { + try { + payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.QUIT.getEvent(), payloadServerService.getThisServer().getName()); + } catch (Exception ex) { + payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing QUIT event"); } }); } public void publishUpdateName(String oldName, String newName) { - this.payloadServerService.getExecutorService().submit(() -> { - try (Jedis jedis = this.payloadServerService.getDatabase().getJedisResource()) { + this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { + try { Document data = new Document(); data.append("old", oldName); data.append("new", newName); - jedis.publish(ServerEvent.UPDATE_NAME.getEvent(), data.toJson()); - } - catch (Exception ex) { - payloadServerService.getDatabase().getErrorService().capture(ex, "Server Manager: Error publishing UPDATE_NAME event"); + payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.UPDATE_NAME.getEvent(), data.toJson()); + } catch (Exception ex) { + payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing UPDATE_NAME event"); } }); } diff --git a/src/main/java/com/jonahseguin/payload/server/ServerSubscriber.java b/src/main/java/com/jonahseguin/payload/server/ServerSubscriber.java deleted file mode 100644 index 01e5173..0000000 --- a/src/main/java/com/jonahseguin/payload/server/ServerSubscriber.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. - * www.jonahseguin.com - */ - -package com.jonahseguin.payload.server; - -import org.bson.Document; -import redis.clients.jedis.JedisPubSub; - -public class ServerSubscriber extends JedisPubSub { - - private final PayloadServerService payloadServerService; - - public ServerSubscriber(PayloadServerService payloadServerService) { - this.payloadServerService = payloadServerService; - } - - @Override - public void onMessage(String channel, String message) { - ServerEvent event = ServerEvent.fromChannel(channel); - if (event != null) { - if (event.equals(ServerEvent.JOIN)) { - if (!message.equalsIgnoreCase(this.payloadServerService.getThisServer().getName())) { - payloadServerService.handleJoin(message); - } - } else if (event.equals(ServerEvent.QUIT)) { - if (!message.equalsIgnoreCase(this.payloadServerService.getThisServer().getName())) { - payloadServerService.handleQuit(message); - } - } else if (event.equals(ServerEvent.PING)) { - if (!message.equalsIgnoreCase(this.payloadServerService.getThisServer().getName())) { - payloadServerService.handlePing(message); - } - } else if (event.equals(ServerEvent.UPDATE_NAME)) { - Document data = Document.parse(message); - String oldName = data.getString("old"); - String newName = data.getString("new"); - payloadServerService.handleUpdateName(oldName, newName); - } - } - } -} From 58d020af3b59cc4855b250e729435a16d896f64f Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 24 Dec 2019 00:54:01 -0700 Subject: [PATCH 08/20] 3.1.0: lettuce.io, remove sync service, remove handshake service, use new server/handshake system, new update system --- pom.xml | 16 +-- .../com/jonahseguin/payload/PayloadLocal.java | 17 +-- .../com/jonahseguin/payload/base/Cache.java | 2 + .../payload/base/PayloadCache.java | 40 +++++- .../base/service/PayloadObjectService.java | 2 +- .../payload/base/service/PayloadService.java | 7 +- .../payload/base/settings/CacheSettings.java | 1 + .../payload/base/type/Payload.java | 2 + .../payload/base/update/PayloadUpdater.java | 128 ++++++++++++++++++ .../payload/database/DatabaseModule.java | 14 -- .../payload/database/redis/PayloadRedis.java | 4 +- .../database/redis/PayloadRedisMonitor.java | 4 +- .../payload/mode/object/PayloadObject.java | 5 + .../payload/mode/profile/PayloadProfile.java | 7 +- .../mode/profile/PayloadProfileCache.java | 21 ++- .../profile/PayloadProfileController.java | 15 +- .../handshake/ProfileHandshakePacket.java | 2 +- .../handshake/ProfileHandshakeService.java | 15 +- .../profile/listener/ProfileListener.java | 82 +++++------ .../payload/server/PayloadServerService.java | 12 +- .../payload/server/ServerPublisher.java | 16 ++- src/main/resources/database.yml | 1 - 22 files changed, 286 insertions(+), 127 deletions(-) create mode 100644 src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java diff --git a/pom.xml b/pom.xml index 1c92b48..5d1cfae 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.5 1.8 1.8 @@ -60,19 +60,6 @@ - - org.projectlombok - lombok-maven-plugin - 1.18.10.0 - - - generate-sources - - delombok - - - - @@ -118,6 +105,7 @@ io.lettuce lettuce-core 5.2.1.RELEASE + compile com.google.guava diff --git a/src/main/java/com/jonahseguin/payload/PayloadLocal.java b/src/main/java/com/jonahseguin/payload/PayloadLocal.java index 8516520..16c6b93 100644 --- a/src/main/java/com/jonahseguin/payload/PayloadLocal.java +++ b/src/main/java/com/jonahseguin/payload/PayloadLocal.java @@ -5,8 +5,6 @@ package com.jonahseguin.payload; -import com.jonahseguin.lang.LangDefinitions; -import com.jonahseguin.lang.LangModule; import com.jonahseguin.payload.base.PayloadPermission; import lombok.Getter; import lombok.Setter; @@ -26,7 +24,7 @@ */ @Getter @Setter -public class PayloadLocal implements LangModule { +public class PayloadLocal { private final PayloadPlugin plugin; private String payloadID = null; @@ -39,16 +37,6 @@ public class PayloadLocal implements LangModule { this.plugin = plugin; } - @Override - public void define(LangDefinitions l) { - l.define("loading-failed", "&cFailed to load payload.yml file, necessary for Payload to function."); - } - - @Override - public String langModule() { - return "local"; - } - public String getPayloadID() { return payloadID; } @@ -134,8 +122,7 @@ public boolean isFirstStartup() { } private void handleError() { - plugin.setLocked(true); // Lock server - plugin.alert(PayloadPermission.ADMIN, langModule(), "loading-failed"); // Alert online staff if any + console + plugin.alert(PayloadPermission.ADMIN, "&cFailed to load payload.yml file, necessary for Payload to function."); // Alert online staff if any + console } } diff --git a/src/main/java/com/jonahseguin/payload/base/Cache.java b/src/main/java/com/jonahseguin/payload/base/Cache.java index 58a6179..43102ed 100644 --- a/src/main/java/com/jonahseguin/payload/base/Cache.java +++ b/src/main/java/com/jonahseguin/payload/base/Cache.java @@ -25,6 +25,8 @@ public interface Cache> extends Service, DatabaseDependent { + boolean pushUpdate(@Nonnull X payload); + Optional get(@Nonnull K key); Optional getFromCache(@Nonnull K key); diff --git a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java index 09f249c..ceb2e07 100644 --- a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java +++ b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java @@ -18,6 +18,7 @@ import com.jonahseguin.payload.base.task.PayloadAutoSaveTask; import com.jonahseguin.payload.base.type.Payload; import com.jonahseguin.payload.base.type.PayloadInstantiator; +import com.jonahseguin.payload.base.update.PayloadUpdater; import com.jonahseguin.payload.database.DatabaseService; import com.jonahseguin.payload.server.ServerService; import lombok.Getter; @@ -52,6 +53,7 @@ public abstract class PayloadCache> implements Comparabl @Inject protected DatabaseService database; @Inject protected PLangService lang; @Inject protected ServerService serverService; + protected PayloadUpdater updater; protected ErrorService errorService; protected PayloadInstantiator instantiator; protected boolean debug = true; @@ -98,7 +100,14 @@ public final boolean start() { boolean success = true; if (!initialize()) { success = false; - errorService.capture("Failed to initialize internally for cache " + name); + errorService.capture("Failed to initialize internally for cache: " + name); + } + updater = new PayloadUpdater<>(this, database); + if (getSettings().isEnableUpdater() && mode.equals(PayloadMode.NETWORK_NODE)) { + if (!updater.start()) { + success = false; + errorService.capture("Failed to start Payload Updater for cache: " + name); + } } autoSaveTask.start(); running = true; @@ -118,6 +127,15 @@ public final boolean shutdown() { success = false; } + if (updater != null) { + if (updater.isRunning()) { + if (!updater.shutdown()) { + success = false; + errorService.capture("Failed to shutdown Payload Updater for cache: " + name); + } + } + } + autoSaveTask.stop(); running = false; return success; @@ -135,8 +153,28 @@ public final boolean shutdown() { */ protected abstract boolean terminate(); + @Override + public boolean pushUpdate(@Nonnull X payload) { + Preconditions.checkNotNull(payload, "Payload cannot be null for pushUpdate"); + if (!getSettings().isEnableUpdater()) { + errorService.debug("Not pushing update for Payload " + keyToString(payload.getIdentifier()) + ": Updater is not enabled!"); + return true; + } + if (!mode.equals(PayloadMode.NETWORK_NODE)) { + errorService.debug("Not pushing update for Payload " + keyToString(payload.getIdentifier()) + ": Cache mode is not Network Node!"); + return true; + } + if (updater != null) { + return updater.pushUpdate(payload); + } else { + errorService.capture("Couldn't pushUpdate for Payload " + keyToString(payload.getIdentifier()) + ": PayloadUpdater is null!"); + return false; + } + } + /** * Get a number of objects currently stored locally in this cache + * * @return int number of objects cached */ @Override diff --git a/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java b/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java index e5eb021..9a17b0d 100644 --- a/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java +++ b/src/main/java/com/jonahseguin/payload/base/service/PayloadObjectService.java @@ -8,7 +8,7 @@ import com.jonahseguin.payload.mode.object.ObjectCache; import com.jonahseguin.payload.mode.object.PayloadObject; -public interface PayloadObjectService extends PayloadService { +public interface PayloadObjectService extends PayloadService { ObjectCache cache(); diff --git a/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java b/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java index ad5ea81..0c44b84 100644 --- a/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java +++ b/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java @@ -12,23 +12,18 @@ import javax.annotation.Nonnull; import java.util.Collection; import java.util.Optional; -import java.util.concurrent.Future; public interface PayloadService> extends Service { Optional get(@Nonnull K key); - Future> getAsync(@Nonnull K key); - Optional getFromCache(@Nonnull K key); Optional getFromDatabase(@Nonnull K key); boolean save(@Nonnull X payload); - Future saveAsync(@Nonnull X payload); - - boolean saveNoSync(@Nonnull X payload); + void saveAsync(@Nonnull X payload); void cache(@Nonnull X payload); diff --git a/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java b/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java index 0aaacac..ffa3bee 100644 --- a/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java +++ b/src/main/java/com/jonahseguin/payload/base/settings/CacheSettings.java @@ -16,5 +16,6 @@ public abstract class CacheSettings { private int autoSaveIntervalSeconds = 600; private boolean serverSpecific = false; // should we associate each object with a server, and only cache objects that match this server + private boolean enableUpdater = true; } diff --git a/src/main/java/com/jonahseguin/payload/base/type/Payload.java b/src/main/java/com/jonahseguin/payload/base/type/Payload.java index 668bcee..f78913d 100644 --- a/src/main/java/com/jonahseguin/payload/base/type/Payload.java +++ b/src/main/java/com/jonahseguin/payload/base/type/Payload.java @@ -78,4 +78,6 @@ public interface Payload { boolean hasValidHandshake(); + void onReceiveUpdate(); + } diff --git a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java new file mode 100644 index 0000000..0c56c02 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.base.update; + +import com.google.common.base.Preconditions; +import com.jonahseguin.payload.base.Cache; +import com.jonahseguin.payload.base.Service; +import com.jonahseguin.payload.base.type.Payload; +import com.jonahseguin.payload.database.DatabaseService; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; +import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; +import org.bson.Document; + +import javax.annotation.Nonnull; + +public class PayloadUpdater> implements Service { + + private static final String KEY_SOURCE_SERVER = "sourceServer"; + private static final String KEY_IDENTIFIER = "identifier"; + + private final Cache cache; + private final DatabaseService database; + private RedisPubSubReactiveCommands reactive = null; + private boolean running = false; + private final String channel; + + public PayloadUpdater(Cache cache, DatabaseService database) { + this.cache = cache; + this.database = database; + this.channel = "payload-update-" + cache.getName(); + } + + @Override + public boolean start() { + Preconditions.checkState(!running, "Payload Updater is already running for cache: " + cache.getName()); + boolean sub = subscribe(); + if (!sub) { + cache.getErrorService().capture("Failed to subscribe to channel " + this.channel + " in PayloadUpdater for cache: " + cache.getName()); + } + running = true; + return sub; + } + + @Override + public boolean shutdown() { + Preconditions.checkState(running, "Payload Updater is not running for cache: " + cache.getName()); + if (reactive != null) { + reactive.unsubscribe(channel); + } + running = false; + return false; + } + + private boolean subscribe() { + try { + StatefulRedisPubSubConnection connection = database.getRedisPubSub(); + reactive = connection.reactive(); + + reactive.subscribe(channel).subscribe(); + + reactive.observeChannels() + .filter(pm -> !pm.getChannel().equals(channel)) + .doOnNext(patternMessage -> { + if (patternMessage != null && patternMessage.getMessage() != null) { + receiveUpdateRequest(patternMessage.getMessage()); + } + }).subscribe(); + + return true; + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error subscribing in Payload Updater"); + return false; + } + } + + private void receiveUpdateRequest(@Nonnull String msg) { + Preconditions.checkNotNull(msg, "Message (packet) cannot be null in PayloadUpdater for receiveUpdateRequest"); + try { + Document document = Document.parse(msg); + if (document != null) { + String sourceServerString = document.getString(KEY_SOURCE_SERVER); + String identifierString = document.getString(KEY_IDENTIFIER); + if (sourceServerString != null && identifierString != null) { + if (!sourceServerString.equalsIgnoreCase(database.getServerService().getThisServer().getName())) { + // As long as the source server wasn't us + K identifier = cache.keyFromString(identifierString); + cache.runAsync(() -> { + cache.getFromDatabase(identifier).ifPresent(payload -> { + cache.cache(payload); + payload.onReceiveUpdate(); + }); + }); + } + } else { + cache.getErrorService().capture("Source Server or Identifier were null during receiveUpdateRequest in PayloadUpdater for packet: " + msg); + } + } else { + cache.getErrorService().capture("Document parsed was null during receiveUpdateRequest in PayloadUpdater for packet: " + msg); + } + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error with received update request in PayloadUpdater for packet: " + msg); + } + } + + public boolean pushUpdate(@Nonnull X payload) { + try { + Preconditions.checkNotNull(payload, "Payload cannot be null in PayloadUpdater (pushUpdate)"); + final Document document = new Document(); + document.append(KEY_SOURCE_SERVER, database.getServerService().getThisServer().getName()); + document.append(KEY_IDENTIFIER, cache.keyToString(payload.getIdentifier())); + final String json = document.toJson(); + cache.runAsync(() -> database.getRedis().async().publish(channel, json)); + return true; + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Failed to push update from PayloadUpdater for Payload: " + cache.keyToString(payload.getIdentifier())); + return false; + } + } + + + @Override + public boolean isRunning() { + return running; + } +} diff --git a/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java b/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java index 627e917..4c7ef1c 100644 --- a/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java +++ b/src/main/java/com/jonahseguin/payload/database/DatabaseModule.java @@ -13,8 +13,6 @@ import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.annotation.Database; import com.jonahseguin.payload.base.error.ErrorService; -import com.jonahseguin.payload.base.handshake.HandshakeService; -import com.jonahseguin.payload.base.handshake.PayloadHandshakeService; import com.jonahseguin.payload.database.error.DatabaseErrorService; import com.jonahseguin.payload.database.internal.InternalPayloadDatabase; import com.jonahseguin.payload.database.internal.PayloadDatabaseService; @@ -63,16 +61,4 @@ ServerService provideServerService(PayloadAPI api, Injector injector) { } } - @Provides - @Singleton - HandshakeService provideHandshakeService(PayloadAPI api, Injector injector) { - if (api.isHandshakeServiceRegistered(name)) { - return api.getHandshakeService(name); - } else { - HandshakeService service = injector.getInstance(PayloadHandshakeService.class); - api.registerHandshakeService(service); - return service; - } - } - } diff --git a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java index 86715f4..5879f55 100644 --- a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java +++ b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java @@ -50,8 +50,8 @@ public RedisURI getRedisURI() { return RedisURI.create(this.uri); } else { RedisURI.Builder builder = RedisURI.builder() - .withPort(port) .withHost(address) + .withPort(port) .withSsl(ssl); if (auth) { builder.withPassword(password); @@ -61,7 +61,7 @@ public RedisURI getRedisURI() { } public boolean useURI() { - return this.uri != null; + return this.uri != null && uri.length() > 0 && !uri.equalsIgnoreCase("null"); } } diff --git a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java index abfe277..a598eb2 100644 --- a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java +++ b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedisMonitor.java @@ -50,11 +50,11 @@ public void run() { try { String reply = database.getRedis().sync().ping(); - if (reply.contains("OK")) { + if (reply.contains("PONG")) { this.handleConnected(); } else { this.handleDisconnected(); - database.getErrorService().capture("Non-OK ping reply in Redis Monitor: " + reply); + database.getErrorService().capture("Non-PONG ping reply in Redis Monitor: " + reply); } } catch (Exception ex) { diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java index 752ecf4..dc00b13 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObject.java @@ -44,6 +44,11 @@ public boolean hasValidHandshake() { return false; } + @Override + public void onReceiveUpdate() { + + } + @Override public long cachedTimestamp() { return this.cachedTimestamp; diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java index f6ed96b..7d33964 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfile.java @@ -72,12 +72,17 @@ private void onPostPayloadLoad() { this.uuid = UUID.fromString(this.uniqueId); } + @Override + public void onReceiveUpdate() { + + } + @Override public boolean hasValidHandshake() { if (handshakeStartTimestamp > 0) { long ago = System.currentTimeMillis() - handshakeStartTimestamp; long seconds = ago / 1000; - return seconds < 10; + return seconds <= cache.getSettings().getHandshakeTimeoutSeconds() + 1; } return false; } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index b2bc152..ed7498e 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -71,13 +71,18 @@ protected boolean initialize() { success = false; errorService.capture("Failed to start MongoDB store for cache: " + name); } + database.getMorphia().map(NetworkProfile.class); + if (mode.equals(PayloadMode.NETWORK_NODE)) { + if (!handshakeService.start()) { + success = false; + errorService.capture("Failed to start Profile Handshake Service (Network Node mode) for cache: " + name); + } if (!networkService.start()) { success = false; errorService.capture("Failed to start Network Service (Network Node mode) for cache: " + name); } } - database.getMorphia().map(NetworkProfile.class); return success; } @@ -87,6 +92,12 @@ protected boolean terminate() { AtomicInteger failedSaves = new AtomicInteger(0); for (Player player : plugin.getServer().getOnlinePlayers()) { getFromCache(player).ifPresent(payload -> { + if (settings.isSetOfflineOnShutdown()) { + getNetworked(payload).ifPresent(networkProfile -> { + networkProfile.markUnloaded(false); + networkService.save(networkProfile); + }); + } if (!save(payload)) { failedSaves.getAndIncrement(); } @@ -103,10 +114,16 @@ protected boolean terminate() { success = false; } if (mode.equals(PayloadMode.NETWORK_NODE)) { + if (handshakeService.isRunning()) { + if (!handshakeService.shutdown()) { + success = false; + errorService.capture("Failed to shutdown Profile Handshake Service (Network Node mode) for cache: " + name); + } + } if (networkService.isRunning()) { if (!networkService.shutdown()) { success = false; - errorService.capture("Failed to start Network Service (Network Node mode) for cache: " + name); + errorService.capture("Failed to shutdown Network Service (Network Node mode) for cache: " + name); } } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java index c6b9174..310ad10 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileController.java @@ -92,16 +92,19 @@ public Optional cache() { @Override public void uncache(@Nonnull X payload, boolean switchingServers) { if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { + Optional o = cache.getNetworkService().get(payload); + if (o.isPresent()) { + NetworkProfile networkProfile = o.get(); + networkProfile.markUnloaded(switchingServers); + cache.getNetworkService().save(networkProfile); + cache.getErrorService().debug("Set NetworkProfile to offline for " + payload.getName()); + } else { + cache.getErrorService().capture("Couldn't set NetworkProfile to offline for player " + payload.getName() + ", no NetworkProfile was found"); + } if (cache.isCached(payload.getUniqueId())) { cache.uncache(payload.getUniqueId()); } } - Optional o = cache.getNetworkService().get(payload.getUniqueId()); - if (o.isPresent()) { - NetworkProfile networkProfile = o.get(); - networkProfile.markUnloaded(switchingServers); - cache.runAsync(() -> cache.getNetworkService().save(networkProfile)); - } } private Optional cacheStandalone() { diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java index 628fd3c..62e63b0 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakePacket.java @@ -45,7 +45,7 @@ public static ProfileHandshakePacket fromJSON(@Nonnull String json) { public Document toDocument() { Document document = new Document(); document.append(KEY_TARGET_SERVER, targetServer); - document.append(KEY_UUID, uuid); + document.append(KEY_UUID, uuid.toString()); document.append(KEY_SENDER_SERVER, senderServer); return document; } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java index fe5f046..aac652f 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/handshake/ProfileHandshakeService.java @@ -55,7 +55,7 @@ public boolean shutdown() { reactive.unsubscribe(channelRequest, channelReply); } running = false; - return false; + return true; } @Override @@ -78,7 +78,7 @@ private boolean subscribe() { if (packet.getTargetServer().equalsIgnoreCase(database.getServerService().getThisServer().getName())) { if (patternMessage.getChannel().equals(channelRequest)) { handleRequest(packet); - } else if (patternMessage.getChannel().equals(channelRequest)) { + } else if (patternMessage.getChannel().equals(channelReply)) { handleReply(packet); } } @@ -98,7 +98,8 @@ private void sendReply(@Nonnull ProfileHandshakePacket requestPacket) { requestPacket.setSenderServer(database.getServerService().getThisServer().getName()); String json = requestPacket.toDocument().toJson(); Preconditions.checkNotNull(json, "JSON cannot be null for sendReply in ProfileHandshakeService"); - database.getRedisPubSub().async().publish(channelReply, json); + database.getRedis().async().publish(channelReply, json); + cache.getErrorService().debug("Sending reply for handshake for UUID " + requestPacket.getUuid() + " [" + requestPacket.getSenderServer() + " -> " + requestPacket.getTargetServer() + "]"); } private void handleRequest(@Nonnull final ProfileHandshakePacket packet) { @@ -106,10 +107,14 @@ private void handleRequest(@Nonnull final ProfileHandshakePacket packet) { // Another server is being joined by the player (who is assumingly on this server) // - check if they're online, if they are: save them // - after save (or if they're not online) send reply + cache.getErrorService().debug("Received handshake request for " + packet.getUuid() + " [" + packet.getSenderServer() + " -> " + packet.getTargetServer() + "]"); final Player player = cache.getPlugin().getServer().getPlayer(packet.getUuid()); cache.runAsync(() -> { if (player != null && player.isOnline()) { - cache.getFromCache(player).ifPresent(cache::save); + cache.getFromCache(player).ifPresent(profile -> { + profile.setHandshakeStartTimestamp(System.currentTimeMillis()); + cache.save(profile); + }); } sendReply(packet); }); @@ -132,7 +137,7 @@ public void handshake(@Nonnull PayloadProfileController controller, PayloadSe controller.setHandshakeTimedOut(false); controller.setHandshakeComplete(false); controller.setHandshakeRequestStartTime(System.currentTimeMillis()); - database.getRedisPubSub().async().publish(channelRequest, json); + database.getRedis().async().publish(channelRequest, json); } } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index 99d26c2..932ae0b 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -15,7 +15,6 @@ import com.jonahseguin.payload.mode.profile.ProfileCache; import com.jonahseguin.payload.mode.profile.event.PayloadProfileLogoutEvent; import com.jonahseguin.payload.mode.profile.event.PayloadProfileSwitchServersEvent; -import com.jonahseguin.payload.mode.profile.network.NetworkProfile; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -83,59 +82,48 @@ public void onProfileQuit(final PlayerQuitEvent event) { api.getSortedCachesByDependsReversed().forEach(c -> { if (c instanceof PayloadProfileCache) { PayloadProfileCache cache = (PayloadProfileCache) c; - cache.runAsync(() -> { - if (cache.getMode().equals(PayloadMode.STANDALONE)) { - // save on quit in standalone mode - cache.getPool().submit(() -> { - Optional o = cache.getFromCache(player.getUniqueId()); - if (o.isPresent()) { - PayloadProfile profile = o.get(); - - PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); - cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); - - Optional oNP = cache.getNetworked(profile); - oNP.ifPresent(networkProfile -> { - networkProfile.setOnline(false); - networkProfile.setLastSeen(System.currentTimeMillis()); - cache.runAsync(() -> cache.getNetworkService().save(networkProfile)); - }); - - profile.uninitializePlayer(); - if (!cache.save(profile)) { - cache.getErrorService().capture("Error saving profile on quit: " + player.getName()); - } - cache.removeController(profile.getUniqueId()); - } - }); - } else if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { - Optional o = cache.getFromCache(player.getUniqueId()); - if (o.isPresent()) { - PayloadProfile profile = o.get(); - profile.uninitializePlayer(); - if (!profile.hasValidHandshake()) { - PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); - cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); - - // Not switching servers (no incoming handshake) -- we can assume they are actually - // Logging out, and not switching servers + if (cache.getMode().equals(PayloadMode.STANDALONE)) { + // save on quit in standalone mode + Optional o = cache.getFromCache(player.getUniqueId()); + if (o.isPresent()) { + PayloadProfile profile = o.get(); + + PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); + cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + + profile.uninitializePlayer(); + cache.saveAsync(profile); + cache.removeController(profile.getUniqueId()); + } + } else if (cache.getMode().equals(PayloadMode.NETWORK_NODE)) { + Optional o = cache.getFromCache(player.getUniqueId()); + if (o.isPresent()) { + PayloadProfile profile = o.get(); + profile.uninitializePlayer(); + if (!profile.hasValidHandshake()) { + PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); + cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + + // Not switching servers (no incoming handshake) -- we can assume they are actually + // Logging out, and not switching servers + cache.runAsync(() -> { cache.save(profile); cache.controller(event.getPlayer().getUniqueId()).uncache(profile, false); cache.removeController(player.getUniqueId()); cache.getErrorService().debug("Saving player " + player.getName() + " on logout (not switching servers)"); - } else { - PayloadProfileSwitchServersEvent payloadEvent = new PayloadProfileSwitchServersEvent(profile); - cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); - - cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); - cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); - } + }); } else { - // This shouldn't happen - cache.getErrorService().debug("Profile null during logout for Payload '" + player.getName() + "': could not set online=false"); + PayloadProfileSwitchServersEvent payloadEvent = new PayloadProfileSwitchServersEvent(profile); + cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + + cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); + cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); } + } else { + // This shouldn't happen + cache.getErrorService().debug("Profile null during logout for Payload '" + player.getName() + "': could not set online=false"); } - }); + } } }); } diff --git a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java index 161e5e5..b0ce41c 100644 --- a/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java +++ b/src/main/java/com/jonahseguin/payload/server/PayloadServerService.java @@ -30,7 +30,7 @@ public class PayloadServerService implements Runnable, ServerService { public static final long ASSUME_OFFLINE_SECONDS = 60; - public static final long PING_FREQUENCY_SECONDS = 20; + public static final long PING_FREQUENCY_SECONDS = 30; private final PayloadAPI api; private final String name; @@ -203,10 +203,12 @@ public void run() { this.thisServer.setOnline(true); this.servers.forEach((name, server) -> { if (!name.equalsIgnoreCase(this.thisServer.getName())) { - long pingExpiredAt = System.currentTimeMillis() - (PayloadServerService.ASSUME_OFFLINE_SECONDS * 1000); - if (server.getLastPing() <= pingExpiredAt) { - // Assume they're offline - server.setOnline(false); + if (server.isOnline()) { + long pingExpiredAt = System.currentTimeMillis() - (PayloadServerService.ASSUME_OFFLINE_SECONDS * 1000); + if (server.getLastPing() <= pingExpiredAt) { + // Assume they're offline + server.setOnline(false); + } } } }); diff --git a/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java b/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java index 25f69c1..ff5b712 100644 --- a/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java +++ b/src/main/java/com/jonahseguin/payload/server/ServerPublisher.java @@ -5,6 +5,7 @@ package com.jonahseguin.payload.server; +import com.google.common.base.Preconditions; import org.bson.Document; public class ServerPublisher { @@ -18,7 +19,7 @@ public ServerPublisher(PayloadServerService serverService) { public void publishPing() { this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { try { - payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.PING.getEvent(), payloadServerService.getThisServer().getName()); + payloadServerService.getDatabase().getRedis().async().publish(ServerEvent.PING.getEvent(), payloadServerService.getThisServer().getName()); } catch (Exception ex) { payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing PING event"); } @@ -26,9 +27,16 @@ public void publishPing() { } public void publishJoin() { + Preconditions.checkNotNull(payloadServerService, "Server service"); + Preconditions.checkNotNull(payloadServerService.getDatabase(), "Database"); + Preconditions.checkNotNull(payloadServerService.getDatabase().getRedis(), "Redis"); + Preconditions.checkNotNull(payloadServerService.getDatabase().getRedis().async(), "Redis async"); + Preconditions.checkNotNull(ServerEvent.JOIN.getEvent(), "Join event"); + Preconditions.checkNotNull(payloadServerService.getThisServer(), "thisServer"); + Preconditions.checkNotNull(payloadServerService.getThisServer().getName(), "thisServer#name"); this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { try { - payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.JOIN.getEvent(), payloadServerService.getThisServer().getName()); + payloadServerService.getDatabase().getRedis().async().publish(ServerEvent.JOIN.getEvent(), payloadServerService.getThisServer().getName()); } catch (Exception ex) { payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing JOIN event"); } @@ -38,7 +46,7 @@ public void publishJoin() { public void publishQuit() { this.payloadServerService.getPayloadPlugin().getServer().getScheduler().runTaskAsynchronously(payloadServerService.getPayloadPlugin(), () -> { try { - payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.QUIT.getEvent(), payloadServerService.getThisServer().getName()); + payloadServerService.getDatabase().getRedis().async().publish(ServerEvent.QUIT.getEvent(), payloadServerService.getThisServer().getName()); } catch (Exception ex) { payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing QUIT event"); } @@ -51,7 +59,7 @@ public void publishUpdateName(String oldName, String newName) { Document data = new Document(); data.append("old", oldName); data.append("new", newName); - payloadServerService.getDatabase().getRedisPubSub().async().publish(ServerEvent.UPDATE_NAME.getEvent(), data.toJson()); + payloadServerService.getDatabase().getRedis().async().publish(ServerEvent.UPDATE_NAME.getEvent(), data.toJson()); } catch (Exception ex) { payloadServerService.getDatabase().getErrorService().capture(ex, "Payload Server Service: Error publishing UPDATE_NAME event"); } diff --git a/src/main/resources/database.yml b/src/main/resources/database.yml index 857d7cf..41e7a2b 100644 --- a/src/main/resources/database.yml +++ b/src/main/resources/database.yml @@ -36,7 +36,6 @@ redis: # Or, provide the information below instead of the uri field: address: '127.0.0.1' # IP Address to your redis server port: 6379 # This is the default Redis port - retryTimeout: 5 # Seconds between re-connection attempts in the event the connection fails # If using authentication (recommended) **only if NOT using URI field** auth: From 2ca0231d84d65b765a596806fb9ba2402a6df148 Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 24 Dec 2019 14:11:41 -0700 Subject: [PATCH 09/20] remove prepareUpdate functions, replaced by Cache#pushUpdate --- .../java/com/jonahseguin/payload/base/Cache.java | 4 ---- .../jonahseguin/payload/base/PayloadCache.java | 16 ---------------- .../payload/base/service/PayloadService.java | 5 ----- 3 files changed, 25 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/base/Cache.java b/src/main/java/com/jonahseguin/payload/base/Cache.java index 43102ed..2f8e92e 100644 --- a/src/main/java/com/jonahseguin/payload/base/Cache.java +++ b/src/main/java/com/jonahseguin/payload/base/Cache.java @@ -49,10 +49,6 @@ public interface Cache> extends Service, DatabaseDepende boolean isCached(@Nonnull K key); - void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback> callback); - - void prepareUpdateAsync(@Nonnull X payload, @Nonnull PayloadCallback> callback); - void cacheAll(); @Nonnull diff --git a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java index ceb2e07..30b0725 100644 --- a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java +++ b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java @@ -347,22 +347,6 @@ public boolean isCached(@Nonnull K key) { return getLocalStore().has(key); } - @Override - public void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback> callback) { - Preconditions.checkNotNull(payload); - Preconditions.checkNotNull(callback); - //sync.prepareUpdate(payload, callback); - callback.callback(Optional.of(payload)); // TODO - } - - @Override - public void prepareUpdateAsync(@Nonnull X payload, @Nonnull PayloadCallback> callback) { - Preconditions.checkNotNull(payload); - Preconditions.checkNotNull(callback); - //runAsync(() -> sync.prepareUpdate(payload, callback)); - callback.callback(Optional.of(payload)); // TODO - } - @Override public void setErrorService(@Nonnull ErrorService errorService) { Preconditions.checkNotNull(errorService); diff --git a/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java b/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java index 0c44b84..f74e12d 100644 --- a/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java +++ b/src/main/java/com/jonahseguin/payload/base/service/PayloadService.java @@ -5,7 +5,6 @@ package com.jonahseguin.payload.base.service; -import com.jonahseguin.payload.base.PayloadCallback; import com.jonahseguin.payload.base.Service; import com.jonahseguin.payload.base.type.Payload; @@ -37,10 +36,6 @@ public interface PayloadService> extends Service { boolean isCached(@Nonnull K key); - void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback> callback); - - void prepareUpdateAsync(@Nonnull X payload, @Nonnull PayloadCallback> callback); - void cacheAll(); X create(); From 53668cb4a8020495650df77ce5e5e300d47d0831 Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 24 Dec 2019 14:18:31 -0700 Subject: [PATCH 10/20] Update redis db config --- .../payload/database/redis/PayloadRedis.java | 11 +++-------- src/main/resources/database.yml | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java index 5879f55..522e14d 100644 --- a/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java +++ b/src/main/java/com/jonahseguin/payload/database/redis/PayloadRedis.java @@ -14,24 +14,19 @@ public class PayloadRedis { private final String address; private final int port; - private final boolean auth; private final String password; private final boolean ssl; - private final String uri; - private final int retryTimeout; // Retry connections every X seconds - public static PayloadRedis fromConfig(ConfigurationSection section) { String address = section.getString("address"); int port = section.getInt("port"); - int retryTimeout = section.getInt("retryTimeout"); ConfigurationSection authSection = section.getConfigurationSection("auth"); - boolean auth = authSection.getBoolean("enabled"); + boolean auth = authSection.getBoolean("enabled", false); String password = authSection.getString("password"); - boolean ssl = authSection.getBoolean("ssl"); + boolean ssl = authSection.getBoolean("ssl", false); String uri = section.getString("uri", null); // Default uri to null // The connection URI, if provided, will completely overwrite all other properties. @@ -42,7 +37,7 @@ public static PayloadRedis fromConfig(ConfigurationSection section) { } } - return new PayloadRedis(address, port, auth, password, ssl, uri, retryTimeout); + return new PayloadRedis(address, port, auth, password, ssl, uri); } public RedisURI getRedisURI() { diff --git a/src/main/resources/database.yml b/src/main/resources/database.yml index 41e7a2b..4158463 100644 --- a/src/main/resources/database.yml +++ b/src/main/resources/database.yml @@ -39,5 +39,6 @@ redis: # If using authentication (recommended) **only if NOT using URI field** auth: + ssl: false enabled: false # Change to true if using authentication, which will then use this password to authenticate: password: 'password' # Your password: will be used with redis.auth(password) \ No newline at end of file From 245a03a50f9a2d5d27b44bf8a3e30ae969a6629c Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 24 Dec 2019 20:12:01 -0700 Subject: [PATCH 11/20] New profile updater system to allow editing profiles that might be on a different node in the network --- .../mode/profile/PayloadProfileCache.java | 59 ++++++ .../payload/mode/profile/ProfileCache.java | 5 + .../mode/profile/update/ProfileUpdater.java | 184 ++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java index ed7498e..5af2daa 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/PayloadProfileCache.java @@ -11,6 +11,7 @@ import com.google.inject.Singleton; import com.jonahseguin.payload.PayloadMode; import com.jonahseguin.payload.base.PayloadCache; +import com.jonahseguin.payload.base.PayloadCallback; import com.jonahseguin.payload.base.store.PayloadStore; import com.jonahseguin.payload.base.type.PayloadInstantiator; import com.jonahseguin.payload.base.uuid.UUIDService; @@ -21,6 +22,8 @@ import com.jonahseguin.payload.mode.profile.settings.ProfileCacheSettings; import com.jonahseguin.payload.mode.profile.store.ProfileStoreLocal; import com.jonahseguin.payload.mode.profile.store.ProfileStoreMongo; +import com.jonahseguin.payload.mode.profile.update.ProfileUpdater; +import com.jonahseguin.payload.server.PayloadServer; import lombok.Getter; import org.bukkit.entity.Player; @@ -45,6 +48,7 @@ public class PayloadProfileCache extends PayloadCache< @Inject private UUIDService uuidService; private NetworkService networkService = null; private ProfileHandshakeService handshakeService = null; + private ProfileUpdater profileUpdater = null; public PayloadProfileCache(Injector injector, PayloadInstantiator instantiator, String name, Class payload) { super(injector, instantiator, name, UUID.class, payload); @@ -58,6 +62,7 @@ protected void setupModule() { injector.injectMembers(this); networkService = new RedisNetworkService<>(this); handshakeService = new ProfileHandshakeService<>(this, database); + profileUpdater = new ProfileUpdater<>(this, database); } @Override @@ -82,6 +87,10 @@ protected boolean initialize() { success = false; errorService.capture("Failed to start Network Service (Network Node mode) for cache: " + name); } + if (!profileUpdater.start()) { + success = false; + errorService.capture("Failed to start Profile Updater (Network Node mode) for cache: " + name); + } } return success; } @@ -126,10 +135,60 @@ protected boolean terminate() { errorService.capture("Failed to shutdown Network Service (Network Node mode) for cache: " + name); } } + if (profileUpdater.isRunning()) { + if (!profileUpdater.shutdown()) { + success = false; + errorService.capture("Failed to shutdown Profile Updater (Network Node mode) for cache: " + name); + } + } } return success; } + @Override + public void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback callback) { + Preconditions.checkNotNull(payload, "Payload cannot be null for prepareUpdate"); + Preconditions.checkNotNull(callback, "Callback cannot be null for prepareUpdate"); + if (mode.equals(PayloadMode.NETWORK_NODE)) { + NetworkProfile networkProfile = getNetworked(payload).orElse(null); + if (networkProfile != null) { + if (networkProfile.isOnline()) { + if (!networkProfile.isOnlineThisServer()) { + PayloadServer server = database.getServerService().get(networkProfile.getLastSeenServer()).orElse(null); + if (server != null && server.isOnline()) { + if (!profileUpdater.requestSave(payload, server.getName(), callback)) { + errorService.capture("Failed to requestSave for prepareUpdate for Profile: " + payload.getName()); + } + } else { + callback.callback(payload); + // Their last seen server isn't accurate / the server is offline + } + } else { + callback.callback(payload); + // They're online THIS server, meaning we already have the most up-to-date Profile instance + } + } else { + callback.callback(payload); + // They aren't online at all, meaning we already have the most up-to-date Profile instance + // (since they are saved on logout/shutdown) + } + } else { + // Fail hard. They should have a NetworkProfile + errorService.capture("Couldn't get a NetworkProfile (null) during prepareUpdate for Profile: " + payload.getName()); + callback.callback(null); + } + } else { + // Fail nicely, so that end user doesn't have to check the cache's mode. Instead just callback in STANDALONE + // Since their version will be the most updated anyways. + callback.callback(payload); + } + } + + @Override + public void prepareUpdateAsync(@Nonnull X payload, @Nonnull PayloadCallback callback) { + runAsync(() -> prepareUpdate(payload, callback)); + } + @Override public Optional getNetworked(@Nonnull UUID key) { Preconditions.checkNotNull(key); diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java index 73247c9..6cbf0eb 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/ProfileCache.java @@ -6,6 +6,7 @@ package com.jonahseguin.payload.mode.profile; import com.jonahseguin.payload.base.Cache; +import com.jonahseguin.payload.base.PayloadCallback; import com.jonahseguin.payload.mode.profile.network.NetworkProfile; import com.jonahseguin.payload.mode.profile.network.NetworkService; import com.jonahseguin.payload.mode.profile.settings.ProfileCacheSettings; @@ -18,6 +19,10 @@ public interface ProfileCache extends Cache { + void prepareUpdate(@Nonnull X payload, @Nonnull PayloadCallback callback); + + void prepareUpdateAsync(@Nonnull X payload, @Nonnull PayloadCallback callback); + Optional getNetworked(@Nonnull UUID key); Optional getNetworked(@Nonnull X payload); diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java b/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java new file mode 100644 index 0000000..76407b3 --- /dev/null +++ b/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * www.jonahseguin.com + */ + +package com.jonahseguin.payload.mode.profile.update; + +import com.google.common.base.Preconditions; +import com.jonahseguin.payload.base.PayloadCallback; +import com.jonahseguin.payload.base.Service; +import com.jonahseguin.payload.database.DatabaseService; +import com.jonahseguin.payload.mode.profile.PayloadProfile; +import com.jonahseguin.payload.mode.profile.PayloadProfileCache; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; +import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands; +import org.bson.Document; + +import javax.annotation.Nonnull; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class ProfileUpdater implements Service { + + private static final String KEY_SOURCE_SERVER = "sourceServer"; + private static final String KEY_TARGET_SERVER = "targetServer"; + private static final String KEY_IDENTIFIER = "identifier"; + private static final String KEY_MODE = "mode"; + private static final String MODE_REQUEST = "request"; + private static final String MODE_OK = "ok"; + + private final PayloadProfileCache cache; + private final DatabaseService database; + private RedisPubSubReactiveCommands reactive = null; + private boolean running = false; + private final String channel; + private final ConcurrentMap> waitingReply = new ConcurrentHashMap<>(); + + public ProfileUpdater(PayloadProfileCache cache, DatabaseService database) { + this.cache = cache; + this.database = database; + this.channel = "payload-profile-update-" + cache.getName(); + } + + @Override + public boolean start() { + Preconditions.checkState(!running, "Payload Updater is already running for cache: " + cache.getName()); + boolean sub = subscribe(); + if (!sub) { + cache.getErrorService().capture("Failed to subscribe to channel " + this.channel + " in PayloadUpdater for cache: " + cache.getName()); + } + running = true; + return sub; + } + + @Override + public boolean shutdown() { + Preconditions.checkState(running, "Payload Updater is not running for cache: " + cache.getName()); + if (reactive != null) { + reactive.unsubscribe(channel); + } + waitingReply.clear(); + running = false; + return false; + } + + private boolean subscribe() { + try { + StatefulRedisPubSubConnection connection = database.getRedisPubSub(); + reactive = connection.reactive(); + + reactive.subscribe(channel).subscribe(); + + reactive.observeChannels() + .filter(pm -> !pm.getChannel().equals(channel)) + .doOnNext(patternMessage -> { + try { + String json = patternMessage.getMessage(); + Document document = Document.parse(json); + String targetServerString = document.getString(KEY_TARGET_SERVER); + String sourceServerString = document.getString(KEY_SOURCE_SERVER); + if (!sourceServerString.equalsIgnoreCase(database.getServerService().getThisServer().getName()) && targetServerString.equalsIgnoreCase(database.getServerService().getThisServer().getName())) { + String identifierString = document.getString(KEY_IDENTIFIER); + String mode = document.getString(KEY_MODE); + if (mode.equalsIgnoreCase(MODE_REQUEST)) { + receiveRequestSave(sourceServerString, identifierString); + } else if (mode.equalsIgnoreCase(MODE_OK)) { + receiveReplyOk(sourceServerString, identifierString); + } + } + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error reading incoming packet in ProfileUpdater for packet: '" + patternMessage.getMessage() + "' in channel: " + patternMessage.getChannel()); + } + }).subscribe(); + + return true; + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error subscribing in Payload Updater"); + return false; + } + } + + private void receiveRequestSave(@Nonnull String sourceServerString, @Nonnull String identifierString) { + try { + Preconditions.checkNotNull(sourceServerString, "Source Server cannot be null in ProfileUpdater for receiveRequestSave"); + Preconditions.checkNotNull(identifierString, "Payload Identifier cannot be null in ProfileUpdater for receiveRequestSave"); + UUID uuid = cache.keyFromString(identifierString); + cache.runAsync(() -> { + cache.getFromCache(uuid).ifPresent(cache::save); + replyOk(sourceServerString, identifierString); + }); + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error with incoming save request in ProfileUpdater from server: '" + sourceServerString + "' for Payload with identifier: '" + identifierString + "'"); + } + } + + private void receiveReplyOk(@Nonnull String sourceServerString, @Nonnull String identifierString) { + try { + Preconditions.checkNotNull(sourceServerString, "Source Server cannot be null in ProfileUpdater for receiveReplyOk"); + Preconditions.checkNotNull(identifierString, "Payload Identifier cannot be null in ProfileUpdater for receiveReplyOk"); + if (waitingReply.containsKey(identifierString)) { + PayloadCallback callback = waitingReply.get(identifierString); + waitingReply.remove(identifierString); + UUID uuid = cache.keyFromString(identifierString); + cache.runAsync(() -> { + X payload = cache.getFromDatabase(uuid).orElse(null); + if (payload != null) { + callback.callback(payload); + cache.getErrorService().debug("Received OK reply & called-back from save request for Payload: '" + identifierString + "' from server: '" + sourceServerString + "'"); + } else { + cache.getErrorService().capture("Failed to get Payload from database in ProfileUpdater after receiving OK reply for identifier: '" + identifierString + "'"); + } + }); + } + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error with incoming OK reply in ProfileUpdater from server: '" + sourceServerString + "' for Payload with identifier: '" + identifierString + "'"); + } + } + + private void replyOk(@Nonnull String sourceServerString, @Nonnull String identifierString) { + try { + Preconditions.checkNotNull(sourceServerString, "Source Server cannot be null in ProfileUpdater for replyOk"); + Preconditions.checkNotNull(identifierString, "Payload Identifier cannot be null in ProfileUpdater for replyOk"); + final Document document = new Document(); + document.append(KEY_TARGET_SERVER, sourceServerString); + document.append(KEY_SOURCE_SERVER, database.getServerService().getThisServer().getName()); + document.append(KEY_MODE, MODE_OK); + document.append(KEY_IDENTIFIER, identifierString); + final String json = document.toJson(); + cache.runAsync(() -> database.getRedis().async().publish(channel, json)); + cache.getErrorService().debug("Replied OK for save request for Payload: '" + identifierString + "' from server: '" + sourceServerString + "'"); + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Error with sending OK reply in ProfileUpdater to server: '" + sourceServerString + "' for Payload with identifier: '" + identifierString + "'"); + } + } + + public boolean requestSave(@Nonnull X payload, @Nonnull String targetServerName, @Nonnull PayloadCallback callback) { + try { + Preconditions.checkNotNull(payload, "Payload cannot be null in ProfileUpdater (requestSave)"); + Preconditions.checkNotNull(targetServerName, "Target Server Name cannot be null in ProfileUpdater (requestSave)"); + Preconditions.checkNotNull(callback, "Callback cannot be null in ProfileUpdater (requestSave)"); + final Document document = new Document(); + document.append(KEY_SOURCE_SERVER, database.getServerService().getThisServer().getName()); + document.append(KEY_IDENTIFIER, cache.keyToString(payload.getIdentifier())); + document.append(KEY_MODE, MODE_REQUEST); + document.append(KEY_TARGET_SERVER, targetServerName); + final String json = document.toJson(); + waitingReply.put(payload.getIdentifier().toString(), callback); + cache.runAsync(() -> database.getRedis().async().publish(channel, json)); + cache.getErrorService().debug("Requested save for Payload '" + payload.getIdentifier().toString() + "' from server: '" + targetServerName + "'"); + return true; + } catch (Exception ex) { + cache.getErrorService().capture(ex, "Failed to push update from ProfileUpdater for Payload: " + cache.keyToString(payload.getIdentifier())); + return false; + } + } + + + @Override + public boolean isRunning() { + return running; + } + +} From d21e0a7f9b47e6656fe3938b4782d43f350415a1 Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 24 Dec 2019 20:19:29 -0700 Subject: [PATCH 12/20] Make the updater only apply to Payloads that are already in the cache --- .../payload/base/update/PayloadUpdater.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java index 0c56c02..4d425e2 100644 --- a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java +++ b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java @@ -87,12 +87,14 @@ private void receiveUpdateRequest(@Nonnull String msg) { if (!sourceServerString.equalsIgnoreCase(database.getServerService().getThisServer().getName())) { // As long as the source server wasn't us K identifier = cache.keyFromString(identifierString); - cache.runAsync(() -> { - cache.getFromDatabase(identifier).ifPresent(payload -> { - cache.cache(payload); - payload.onReceiveUpdate(); + if (cache.isCached(identifier)) { + cache.runAsync(() -> { + cache.getFromDatabase(identifier).ifPresent(payload -> { + cache.cache(payload); + payload.onReceiveUpdate(); + }); }); - }); + } } } else { cache.getErrorService().capture("Source Server or Identifier were null during receiveUpdateRequest in PayloadUpdater for packet: " + msg); From db6ac95e438f83025806d23f66c1bb394c94a43d Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 24 Dec 2019 20:27:46 -0700 Subject: [PATCH 13/20] Add an option to force payloads to be cached (even if not cached) for pushUpdate --- .../com/jonahseguin/payload/base/Cache.java | 2 ++ .../payload/base/PayloadCache.java | 7 ++++++- .../payload/base/update/PayloadUpdater.java | 21 ++++++++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/base/Cache.java b/src/main/java/com/jonahseguin/payload/base/Cache.java index 2f8e92e..1fd63f1 100644 --- a/src/main/java/com/jonahseguin/payload/base/Cache.java +++ b/src/main/java/com/jonahseguin/payload/base/Cache.java @@ -27,6 +27,8 @@ public interface Cache> extends Service, DatabaseDepende boolean pushUpdate(@Nonnull X payload); + boolean pushUpdate(@Nonnull X payload, boolean forceLoad); + Optional get(@Nonnull K key); Optional getFromCache(@Nonnull K key); diff --git a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java index 30b0725..f077a93 100644 --- a/src/main/java/com/jonahseguin/payload/base/PayloadCache.java +++ b/src/main/java/com/jonahseguin/payload/base/PayloadCache.java @@ -155,6 +155,11 @@ public final boolean shutdown() { @Override public boolean pushUpdate(@Nonnull X payload) { + return pushUpdate(payload, false); + } + + @Override + public boolean pushUpdate(@Nonnull X payload, boolean forceLoad) { Preconditions.checkNotNull(payload, "Payload cannot be null for pushUpdate"); if (!getSettings().isEnableUpdater()) { errorService.debug("Not pushing update for Payload " + keyToString(payload.getIdentifier()) + ": Updater is not enabled!"); @@ -165,7 +170,7 @@ public boolean pushUpdate(@Nonnull X payload) { return true; } if (updater != null) { - return updater.pushUpdate(payload); + return updater.pushUpdate(payload, forceLoad); } else { errorService.capture("Couldn't pushUpdate for Payload " + keyToString(payload.getIdentifier()) + ": PayloadUpdater is null!"); return false; diff --git a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java index 4d425e2..fc1f4d0 100644 --- a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java +++ b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java @@ -20,6 +20,7 @@ public class PayloadUpdater> implements Service { private static final String KEY_SOURCE_SERVER = "sourceServer"; private static final String KEY_IDENTIFIER = "identifier"; + private static final String KEY_FORCE_LOAD = "forceLoad"; private final Cache cache; private final DatabaseService database; @@ -83,17 +84,16 @@ private void receiveUpdateRequest(@Nonnull String msg) { if (document != null) { String sourceServerString = document.getString(KEY_SOURCE_SERVER); String identifierString = document.getString(KEY_IDENTIFIER); + boolean force = document.getBoolean(KEY_FORCE_LOAD, false); if (sourceServerString != null && identifierString != null) { if (!sourceServerString.equalsIgnoreCase(database.getServerService().getThisServer().getName())) { // As long as the source server wasn't us - K identifier = cache.keyFromString(identifierString); - if (cache.isCached(identifier)) { - cache.runAsync(() -> { - cache.getFromDatabase(identifier).ifPresent(payload -> { - cache.cache(payload); - payload.onReceiveUpdate(); - }); - }); + final K identifier = cache.keyFromString(identifierString); + if (cache.isCached(identifier) || force) { + cache.runAsync(() -> cache.getFromDatabase(identifier).ifPresent(payload -> { + cache.cache(payload); + payload.onReceiveUpdate(); + })); } } } else { @@ -108,11 +108,16 @@ private void receiveUpdateRequest(@Nonnull String msg) { } public boolean pushUpdate(@Nonnull X payload) { + return pushUpdate(payload, false); + } + + public boolean pushUpdate(@Nonnull X payload, boolean force) { try { Preconditions.checkNotNull(payload, "Payload cannot be null in PayloadUpdater (pushUpdate)"); final Document document = new Document(); document.append(KEY_SOURCE_SERVER, database.getServerService().getThisServer().getName()); document.append(KEY_IDENTIFIER, cache.keyToString(payload.getIdentifier())); + document.append(KEY_FORCE_LOAD, force); final String json = document.toJson(); cache.runAsync(() -> database.getRedis().async().publish(channel, json)); return true; From f7a3c5ec9be5f9493349efef6bda7063200710fc Mon Sep 17 00:00:00 2001 From: Jonah Date: Fri, 3 Jan 2020 15:41:55 -0700 Subject: [PATCH 14/20] Debug toggling is now cache-specific --- .../payload/command/commands/CmdDebug.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/command/commands/CmdDebug.java b/src/main/java/com/jonahseguin/payload/command/commands/CmdDebug.java index 6cbfee8..d5bf303 100644 --- a/src/main/java/com/jonahseguin/payload/command/commands/CmdDebug.java +++ b/src/main/java/com/jonahseguin/payload/command/commands/CmdDebug.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -8,6 +8,7 @@ import com.google.inject.Inject; import com.jonahseguin.payload.PayloadAPI; import com.jonahseguin.payload.PayloadPlugin; +import com.jonahseguin.payload.base.Cache; import com.jonahseguin.payload.base.PayloadPermission; import com.jonahseguin.payload.command.CmdArgs; import com.jonahseguin.payload.command.PayloadCommand; @@ -26,18 +27,13 @@ public CmdDebug(PayloadPlugin payloadPlugin, PayloadAPI api) { @Override public void execute(CmdArgs args) { if (args.length() == 0) { - args.msg("&7Payload: Debug is {0} &7(use &e/payload debug [on/off]&7 to toggle)", (payloadPlugin.isDebug() ? "&aon" : "&coff")); + args.msg("&7Payload: Debug is {0} &7(global debug can only be toggled from the config)", (payloadPlugin.isDebug() ? "&aon" : "&coff")); + args.msg("&7Use /payload debug to toggle debug for a specific cache."); } else { - String toggle = args.arg(0).toLowerCase(); - if (toggle.startsWith("on")) { - payloadPlugin.setDebug(true); - api.getCaches().values().forEach(cache -> cache.setDebug(true)); - args.msg("&7Payload: Debug &aon"); - } else if (toggle.startsWith("off")) { - payloadPlugin.setDebug(false); - args.msg("&7Payload: Debug &coff"); - api.getCaches().values().forEach(cache -> cache.setDebug(false)); - } + String name = args.arg(0); + Cache cache = api.getCache(name); + cache.setDebug(!cache.isDebug()); + args.msg("&7Payload: Debug is now {0} &7for the cache: '&6" + cache.getName() + "&7'", (cache.isDebug() ? "&aon" : "&coff")); } } @@ -53,7 +49,7 @@ public String[] aliases() { @Override public String desc() { - return "View debug status and toggle"; + return "Toggle debug status for a cache"; } @Override @@ -63,7 +59,7 @@ public PayloadPermission permission() { @Override public String usage() { - return "[on/off]"; + return ""; } @Override @@ -73,7 +69,7 @@ public boolean playerOnly() { @Override public int minArgs() { - return 0; + return 1; } } From d6fe8298aca79fe47b5606d8e85272fa7bf6ddc9 Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 14 Jan 2020 12:22:16 -0700 Subject: [PATCH 15/20] Fixes to subscribers --- pom.xml | 10 +++++++++- .../payload/base/update/PayloadUpdater.java | 4 ++-- .../payload/mode/profile/update/ProfileUpdater.java | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5d1cfae..58d5c31 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.4.3 + 3.2.1 package @@ -59,6 +59,14 @@ + + + + com.google.common + shaded.com.google.common + + + diff --git a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java index fc1f4d0..c72f461 100644 --- a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java +++ b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java @@ -31,7 +31,7 @@ public class PayloadUpdater> implements Service { public PayloadUpdater(Cache cache, DatabaseService database) { this.cache = cache; this.database = database; - this.channel = "payload-update-" + cache.getName(); + this.channel = "payload-updater-" + cache.getName(); } @Override @@ -63,7 +63,7 @@ private boolean subscribe() { reactive.subscribe(channel).subscribe(); reactive.observeChannels() - .filter(pm -> !pm.getChannel().equals(channel)) + .filter(pm -> pm.getChannel().equals(channel)) .doOnNext(patternMessage -> { if (patternMessage != null && patternMessage.getMessage() != null) { receiveUpdateRequest(patternMessage.getMessage()); diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java b/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java index 76407b3..feee20d 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java @@ -72,7 +72,7 @@ private boolean subscribe() { reactive.subscribe(channel).subscribe(); reactive.observeChannels() - .filter(pm -> !pm.getChannel().equals(channel)) + .filter(pm -> pm.getChannel().equals(channel)) .doOnNext(patternMessage -> { try { String json = patternMessage.getMessage(); From aa29ba89e55b36ce8690051f4cf4e61623d53dac Mon Sep 17 00:00:00 2001 From: Jonah Date: Thu, 16 Jan 2020 15:47:31 -0700 Subject: [PATCH 16/20] Make updater return success properly on shutdown --- .../com/jonahseguin/payload/base/update/PayloadUpdater.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java index c72f461..eb0aa94 100644 --- a/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java +++ b/src/main/java/com/jonahseguin/payload/base/update/PayloadUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -52,7 +52,7 @@ public boolean shutdown() { reactive.unsubscribe(channel); } running = false; - return false; + return true; } private boolean subscribe() { From 6556a5fe4824e10e68f21541efda33ee2b3ce69d Mon Sep 17 00:00:00 2001 From: Jonah Date: Thu, 16 Jan 2020 15:47:55 -0700 Subject: [PATCH 17/20] Also fix ProfileUpdater --- .../payload/mode/profile/update/ProfileUpdater.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java b/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java index feee20d..d46383e 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/update/ProfileUpdater.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -61,7 +61,7 @@ public boolean shutdown() { } waitingReply.clear(); running = false; - return false; + return true; } private boolean subscribe() { From 92567afe366db03af52532be6ae882230698fb08 Mon Sep 17 00:00:00 2001 From: Jonah Date: Sun, 26 Jan 2020 15:34:10 -0700 Subject: [PATCH 18/20] HandlerList methods for events --- .../mode/profile/event/PayloadProfileLogoutEvent.java | 6 +++++- .../profile/event/PayloadProfileSwitchServersEvent.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileLogoutEvent.java b/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileLogoutEvent.java index beb7dae..45a75f1 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileLogoutEvent.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileLogoutEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -28,4 +28,8 @@ public HandlerList getHandlers() { return handlers; } + public static HandlerList getHandlerList() { + return handlers; + } + } diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileSwitchServersEvent.java b/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileSwitchServersEvent.java index 2ed3ac0..13e4aa5 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileSwitchServersEvent.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/event/PayloadProfileSwitchServersEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -27,4 +27,9 @@ public PayloadProfile getProfile() { public HandlerList getHandlers() { return handlers; } + + public static HandlerList getHandlerList() { + return handlers; + } + } From c7842131ecfc3fb356f69d93a0980e8a7b575b9c Mon Sep 17 00:00:00 2001 From: Jonah Date: Sun, 26 Jan 2020 15:41:47 -0700 Subject: [PATCH 19/20] Call profile events before uninit --- .../payload/mode/profile/listener/ProfileListener.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java index 932ae0b..0c85c24 100644 --- a/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java +++ b/src/main/java/com/jonahseguin/payload/mode/profile/listener/ProfileListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -99,11 +99,12 @@ public void onProfileQuit(final PlayerQuitEvent event) { Optional o = cache.getFromCache(player.getUniqueId()); if (o.isPresent()) { PayloadProfile profile = o.get(); - profile.uninitializePlayer(); if (!profile.hasValidHandshake()) { PayloadProfileLogoutEvent payloadEvent = new PayloadProfileLogoutEvent(profile); cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + profile.uninitializePlayer(); + // Not switching servers (no incoming handshake) -- we can assume they are actually // Logging out, and not switching servers cache.runAsync(() -> { @@ -116,6 +117,8 @@ public void onProfileQuit(final PlayerQuitEvent event) { PayloadProfileSwitchServersEvent payloadEvent = new PayloadProfileSwitchServersEvent(profile); cache.getPlugin().getServer().getPluginManager().callEvent(payloadEvent); + profile.uninitializePlayer(); + cache.controller(event.getPlayer().getUniqueId()).uncache(profile, true); cache.getErrorService().debug("Not saving player " + player.getName() + " on quit (is switching servers)"); } From 6be0577c46347e34f905fc2f63c97d89bc60adbf Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 11 Feb 2020 16:28:34 -0700 Subject: [PATCH 20/20] Memory optimization in Object Controller via Weak References --- .../mode/object/PayloadObjectController.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java index 0e61cc0..a868797 100644 --- a/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java +++ b/src/main/java/com/jonahseguin/payload/mode/object/PayloadObjectController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. + * Copyright (c) 2020 Jonah Seguin. All rights reserved. You may not modify, decompile, distribute or use any code/text contained in this document(plugin) without explicit signed permission from Jonah Seguin. * www.jonahseguin.com */ @@ -11,6 +11,7 @@ import lombok.Setter; import javax.annotation.Nonnull; +import java.lang.ref.WeakReference; import java.util.Optional; @Getter @@ -20,7 +21,7 @@ public class PayloadObjectController implements Payload private final PayloadObjectCache cache; private final String identifier; - private X payload = null; + private WeakReference payload = null; private boolean loadedFromLocal = false; PayloadObjectController(@Nonnull PayloadObjectCache cache, String identifier) { @@ -34,13 +35,13 @@ private void load(boolean fromLocal) { if (fromLocal) { Optional local = cache.getFromCache(identifier); if (local.isPresent()) { - payload = local.get(); + payload = new WeakReference<>(local.get()); loadedFromLocal = true; return; } } Optional db = cache.getFromDatabase(identifier); - db.ifPresent(x -> payload = x); + db.ifPresent(x -> payload = new WeakReference<>(x)); } @Override @@ -84,11 +85,15 @@ public Optional cache() { }*/ load(true); - if (payload != null && !loadedFromLocal) { - this.cache.cache(payload); - this.cache.getErrorService().debug("Cached payload " + payload.getIdentifier()); + if (payload != null) { + X p = payload.get(); + if (p != null && !loadedFromLocal) { + this.cache.cache(p); + this.cache.getErrorService().debug("Cached payload " + p.getIdentifier()); + } + return Optional.ofNullable(p); } - return Optional.ofNullable(payload); + return Optional.empty(); } @Override