diff --git a/.github/workflows/publish-modrinth.yml b/.github/workflows/publish-modrinth.yml new file mode 100644 index 00000000..559ecf4b --- /dev/null +++ b/.github/workflows/publish-modrinth.yml @@ -0,0 +1,40 @@ +name: Publish to Modrinth + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + publish: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Build with Gradle + run: ./gradlew build + + - name: Publish to Modrinth + uses: cloudnode-pro/modrinth-publish@v2 + with: + token: ${{ secrets.MODRINTH_TOKEN }} + project: ${{ vars.MODRINTH_PROJECT_ID }} + version: ${{ github.event.release.tag_name }} + changelog: ${{ github.event.release.body }} + loaders: |- + velocity + bungeecord + game-versions: ">=1.8" + files: build/libs/*.jar \ No newline at end of file diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/bungee/BungeeMain.java b/src/main/java/xyz/earthcow/networkjoinmessages/bungee/BungeeMain.java index 2398af88..b452e992 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/bungee/BungeeMain.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/bungee/BungeeMain.java @@ -75,7 +75,7 @@ public void onEnable() { getProxy() .getPluginManager() - .registerListener(this, new PlayerListener(core.getCorePlayerListener())); + .registerListener(this, new PlayerListener(core.getCorePlayerListener(), bungeeLogger)); getProxy() .getPluginManager() @@ -124,16 +124,6 @@ public CoreCommandSender getConsole() { return console; } - @Override - public Core getCore() { - return core; - } - - @Override - public ServerType getServerType() { - return ServerType.BUNGEE; - } - @Override public CoreLogger getCoreLogger() { return bungeeLogger; diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/bungee/listeners/PlayerListener.java b/src/main/java/xyz/earthcow/networkjoinmessages/bungee/listeners/PlayerListener.java index 2aca76fb..b9a49f67 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/bungee/listeners/PlayerListener.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/bungee/listeners/PlayerListener.java @@ -8,6 +8,7 @@ import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import xyz.earthcow.networkjoinmessages.bungee.BungeeMain; +import xyz.earthcow.networkjoinmessages.bungee.abstraction.BungeeLogger; import xyz.earthcow.networkjoinmessages.bungee.abstraction.BungeePlayer; import xyz.earthcow.networkjoinmessages.bungee.abstraction.BungeeServer; import xyz.earthcow.networkjoinmessages.common.abstraction.CorePlayer; @@ -16,9 +17,11 @@ public class PlayerListener implements Listener { private final CorePlayerListener corePlayerListener; + private final BungeeLogger logger; - public PlayerListener(CorePlayerListener corePlayerListener) { + public PlayerListener(CorePlayerListener corePlayerListener, BungeeLogger logger) { this.corePlayerListener = corePlayerListener; + this.logger = logger; } @EventHandler @@ -42,8 +45,10 @@ public void onServerConnected(ServerConnectedEvent e) { @EventHandler public void onDisconnect(PlayerDisconnectEvent event) { // Check that the player disconnected is not a duplicate user session (the same account tries to join the server while already joined) - CorePlayer corePlayer = BungeeMain.getInstance().getOrPutPlayer(new BungeePlayer(event.getPlayer())); - if (corePlayer.getConnectionIdentity() != System.identityHashCode(event.getPlayer())) { + CorePlayer corePlayer = BungeeMain.getInstance().getPlayerManager().getPlayer(event.getPlayer().getUniqueId()); + if (corePlayer == null || corePlayer.getConnectionIdentity() != System.identityHashCode(event.getPlayer())) { + logger.debug("Ignoring disconnect event for " + (corePlayer == null ? "null" : corePlayer.getName()) + + " (" + event.getPlayer().getName() + ") - null or duplicate player identity"); return; } diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlayer.java b/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlayer.java index 59a06000..2dab426c 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlayer.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlayer.java @@ -1,5 +1,6 @@ package xyz.earthcow.networkjoinmessages.common.abstraction; +import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.audience.Audience; @@ -7,12 +8,16 @@ import org.jetbrains.annotations.Nullable; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; @Getter @Setter public abstract class CorePlayer implements CoreCommandSender { // Fields + @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) + private AtomicBoolean connected = new AtomicBoolean(false); private CoreBackendServer lastKnownConnectedServer; - private boolean disconnecting = false; + @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) + private AtomicBoolean disconnecting = new AtomicBoolean(false); private String cachedLeaveMessage; private Audience audience; private boolean premiumVanishHidden = false; @@ -24,6 +29,18 @@ public CorePlayer(CoreBackendServer lastKnownConnectedServer, Audience audience) this.audience = audience; } + public boolean isConnected() { + return connected.get(); + } + + public void setConnected(boolean value) { + connected.set(value); + } + + public boolean markDisconnecting() { + return disconnecting.compareAndSet(false, true); + } + // Abstract @NotNull public abstract UUID getUniqueId(); diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlugin.java b/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlugin.java index cafe8632..da02d5df 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlugin.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/CorePlugin.java @@ -1,6 +1,5 @@ package xyz.earthcow.networkjoinmessages.common.abstraction; -import xyz.earthcow.networkjoinmessages.common.Core; import xyz.earthcow.networkjoinmessages.common.modules.DiscordIntegration; import java.io.File; @@ -8,11 +7,8 @@ import java.util.UUID; public interface CorePlugin { - Core getCore(); - void disable(); - ServerType getServerType(); File getDataFolder(); CoreLogger getCoreLogger(); diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/ServerType.java b/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/ServerType.java deleted file mode 100644 index 686a96b1..00000000 --- a/src/main/java/xyz/earthcow/networkjoinmessages/common/abstraction/ServerType.java +++ /dev/null @@ -1,21 +0,0 @@ -package xyz.earthcow.networkjoinmessages.common.abstraction; - -public enum ServerType { - BUNGEE ("BungeeCord"), - VELOCITY("Velocity"); - - private final String displayName; - - ServerType() { - this(null); - } - - ServerType(String displayName) { - this.displayName = displayName; - } - - @Override - public String toString() { - return (displayName != null) ? displayName : super.toString(); - } -} \ No newline at end of file diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/common/listeners/CorePlayerListener.java b/src/main/java/xyz/earthcow/networkjoinmessages/common/listeners/CorePlayerListener.java index 20fa79be..d937ad33 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/common/listeners/CorePlayerListener.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/common/listeners/CorePlayerListener.java @@ -87,11 +87,11 @@ public void onPreConnect(@NotNull CorePlayer player, @Nullable String previousSe public void onServerConnected(@NotNull CorePlayer player, @NotNull CoreBackendServer server, @Nullable CoreBackendServer previousServer) { plugin.runTaskAsync(() -> { - if (!stateStore.isConnected(player)) { - handleJoin(player, server); - } else { + if (player.isConnected()) { boolean fromLimbo = plugin.hasLimbo() && previousServer == null; handleSwap(player, server, fromLimbo); + } else { + handleJoin(player, server); } }); } @@ -100,11 +100,10 @@ public void onServerConnected(@NotNull CorePlayer player, @NotNull CoreBackendSe * Called when a player disconnects from the network. */ public void onDisconnect(@NotNull CorePlayer player) { - if (player.isDisconnecting()) { + if (!player.markDisconnecting()) { plugin.getCoreLogger().debug("Duplicate disconnect ignored for " + player.getName()); return; } - player.setDisconnecting(true); if (shouldSkipLeave(player)) { cleanup(player); @@ -119,7 +118,7 @@ public void onDisconnect(@NotNull CorePlayer player) { private void handleJoin(@NotNull CorePlayer player, @NotNull CoreBackendServer server) { stateStore.loadData(player.getUniqueId(), player.getName()); - stateStore.setConnected(player, true); + player.setConnected(true); player.setLastKnownConnectedServer(server); PremiumVanish pv = plugin.getVanishAPI(); @@ -243,7 +242,7 @@ private boolean shouldSkipSwap(CorePlayer player, String from, String to, boolea private boolean shouldSkipLeave(CorePlayer player) { if (player.getCurrentServer() == null) return true; - if (!stateStore.isConnected(player)) { + if (!player.isConnected()) { plugin.getCoreLogger().debug("Skipping leave for " + player.getName() + " — not marked as connected"); return true; } @@ -306,7 +305,6 @@ private void fireLeaveEvent(CorePlayer player, String serverName, String message private void cleanup(CorePlayer player) { plugin.getPlayerManager().removePlayer(player.getUniqueId()); - stateStore.setConnected(player, false); leaveMessageCache.stopFor(player); } } diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStore.java b/src/main/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStore.java index 914f7e82..0419078c 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStore.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStore.java @@ -26,7 +26,6 @@ public final class PlayerStateStore { private final Map previousServer = new ConcurrentHashMap<>(); private final Map silentState = new ConcurrentHashMap<>(); - private final Set onlinePlayers = ConcurrentHashMap.newKeySet(); private final Set noJoinMessage = ConcurrentHashMap.newKeySet(); private final Set noLeaveMessage = ConcurrentHashMap.newKeySet(); private final Set noSwapMessage = ConcurrentHashMap.newKeySet(); @@ -101,17 +100,6 @@ private Boolean determineSaveState(Set set, boolean ignoreByDefault, UUID return null; } - // --- Online tracking --- - - public boolean isConnected(CorePlayer player) { - return onlinePlayers.contains(player.getUniqueId()); - } - - public void setConnected(CorePlayer player, boolean connected) { - if (connected) onlinePlayers.add(player.getUniqueId()); - else onlinePlayers.remove(player.getUniqueId()); - } - // --- Previous server --- public String getFrom(CorePlayer player) { diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/velocity/VelocityMain.java b/src/main/java/xyz/earthcow/networkjoinmessages/velocity/VelocityMain.java index 3ea6adb4..cc82a6eb 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/velocity/VelocityMain.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/velocity/VelocityMain.java @@ -106,7 +106,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { velocityLogger.info("Successfully hooked into PremiumVanish!"); } - proxy.getEventManager().register(this, new PlayerListener(core.getCorePlayerListener())); + proxy.getEventManager().register(this, new PlayerListener(core.getCorePlayerListener(), velocityLogger)); registerCommands(); @@ -261,16 +261,6 @@ public PremiumVanish getVanishAPI() { return premiumVanish; } - @Override - public ServerType getServerType() { - return ServerType.VELOCITY; - } - - @Override - public Core getCore() { - return core; - } - @Override public CoreCommandSender getConsole() { return console; diff --git a/src/main/java/xyz/earthcow/networkjoinmessages/velocity/listeners/PlayerListener.java b/src/main/java/xyz/earthcow/networkjoinmessages/velocity/listeners/PlayerListener.java index 43ab428f..937fcc60 100644 --- a/src/main/java/xyz/earthcow/networkjoinmessages/velocity/listeners/PlayerListener.java +++ b/src/main/java/xyz/earthcow/networkjoinmessages/velocity/listeners/PlayerListener.java @@ -7,15 +7,18 @@ import xyz.earthcow.networkjoinmessages.common.abstraction.CorePlayer; import xyz.earthcow.networkjoinmessages.common.listeners.CorePlayerListener; import xyz.earthcow.networkjoinmessages.velocity.VelocityMain; +import xyz.earthcow.networkjoinmessages.velocity.abstraction.VelocityLogger; import xyz.earthcow.networkjoinmessages.velocity.abstraction.VelocityPlayer; import xyz.earthcow.networkjoinmessages.velocity.abstraction.VelocityServer; public class PlayerListener { private final CorePlayerListener corePlayerListener; + private final VelocityLogger logger; - public PlayerListener(CorePlayerListener corePlayerListener) { + public PlayerListener(CorePlayerListener corePlayerListener, VelocityLogger logger) { this.corePlayerListener = corePlayerListener; + this.logger = logger; } @Subscribe @@ -36,11 +39,12 @@ public void onServerConnected(ServerConnectedEvent event) { @Subscribe public void onDisconnect(DisconnectEvent event) { // Check that the player disconnected is not a duplicate user session (the same account tries to join the server while already joined) - CorePlayer corePlayer = VelocityMain.getInstance().getOrPutPlayer(new VelocityPlayer(event.getPlayer())); - if (corePlayer.getConnectionIdentity() != System.identityHashCode(event.getPlayer())) { + CorePlayer corePlayer = VelocityMain.getInstance().getPlayerManager().getPlayer(event.getPlayer().getUniqueId()); + if (corePlayer == null || corePlayer.getConnectionIdentity() != System.identityHashCode(event.getPlayer())) { + logger.debug("Ignoring disconnect event for " + (corePlayer == null ? "null" : corePlayer.getName()) + + " (" + event.getPlayer().getUsername() + ") - null or duplicate player identity"); return; } - corePlayerListener.onDisconnect(corePlayer); } } diff --git a/src/test/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStoreTest.java b/src/test/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStoreTest.java index 545f7efe..83e46d86 100644 --- a/src/test/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStoreTest.java +++ b/src/test/java/xyz/earthcow/networkjoinmessages/common/player/PlayerStateStoreTest.java @@ -290,31 +290,6 @@ void getSuppressedPlayers_firstJoinMapsToJoinSet() { "FIRST_JOIN should return the same set as JOIN"); } - // ----------------------------------------------------------------------- - // isConnected / setConnected - // ----------------------------------------------------------------------- - - @Test - void setConnected_true_marksPlayerOnline() { - PlayerStateStore stateStore = new PlayerStateStore(plugin, config, store); - stateStore.setConnected(player, true); - assertTrue(stateStore.isConnected(player)); - } - - @Test - void setConnected_false_marksPlayerOffline() { - PlayerStateStore stateStore = new PlayerStateStore(plugin, config, store); - stateStore.setConnected(player, true); - stateStore.setConnected(player, false); - assertFalse(stateStore.isConnected(player)); - } - - @Test - void isConnected_unknownPlayer_returnsFalse() { - PlayerStateStore stateStore = new PlayerStateStore(plugin, config, store); - assertFalse(stateStore.isConnected(player)); - } - // ----------------------------------------------------------------------- // getFrom / setFrom // -----------------------------------------------------------------------