From 27cdcf324da3151b1a77e7fcbc95d03bf3c9400f Mon Sep 17 00:00:00 2001 From: samo_lego <34912839+samolego@users.noreply.github.com> Date: Thu, 16 Apr 2020 14:26:27 +0200 Subject: [PATCH] Fixed NPE that could occur when stopping server. --- .../org/samo_lego/simpleauth/SimpleAuth.java | 9 ++-- .../simpleauth/commands/AuthCommand.java | 2 +- .../simpleauth/event/AuthEventHandler.java | 41 ++++++++++++++---- .../entity/player/PrePlayerJoinCallback.java | 28 ++++++++++++ .../simpleauth/mixin/MixinPlayerManager.java | 24 +++++++++++ .../mixin/MixinServerLoginNetworkHandler.java | 43 ------------------- .../storage/SimpleAuthDatabase.java | 6 +++ src/main/resources/mixins.simpleauth.json | 1 - 8 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 src/main/java/org/samo_lego/simpleauth/event/entity/player/PrePlayerJoinCallback.java delete mode 100644 src/main/java/org/samo_lego/simpleauth/mixin/MixinServerLoginNetworkHandler.java diff --git a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java index 7d1e2ce..9a1a962 100644 --- a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java +++ b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java @@ -5,6 +5,7 @@ import net.fabricmc.fabric.api.event.player.*; import net.fabricmc.fabric.api.event.server.ServerStopCallback; import net.fabricmc.fabric.api.registry.CommandRegistry; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; @@ -12,10 +13,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.samo_lego.simpleauth.commands.*; import org.samo_lego.simpleauth.event.AuthEventHandler; -import org.samo_lego.simpleauth.event.entity.player.ChatCallback; -import org.samo_lego.simpleauth.event.entity.player.PlayerJoinServerCallback; -import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback; -import org.samo_lego.simpleauth.event.entity.player.PlayerMoveCallback; +import org.samo_lego.simpleauth.event.entity.player.*; import org.samo_lego.simpleauth.event.item.DropItemCallback; import org.samo_lego.simpleauth.event.item.TakeItemCallback; import org.samo_lego.simpleauth.storage.AuthConfig; @@ -75,6 +73,7 @@ public class SimpleAuth implements DedicatedServerModInitializer { }); // Registering the events + PrePlayerJoinCallback.EVENT.register(AuthEventHandler::checkCanPlayerJoinServer); PlayerJoinServerCallback.EVENT.register(AuthEventHandler::onPlayerJoin); PlayerLeaveServerCallback.EVENT.register(AuthEventHandler::onPlayerLeave); DropItemCallback.EVENT.register(AuthEventHandler::onDropItem); @@ -115,6 +114,8 @@ public class SimpleAuth implements DedicatedServerModInitializer { // De-authenticates player public static void deauthenticatePlayer(ServerPlayerEntity player) { + if(db.isClosed()) + return; // Marking player as not authenticated, (re)setting login tries to zero String uuid = player.getUuidAsString(); SimpleAuth.deauthenticatedUsers.put(uuid, new PlayerCache(uuid, player.getIp())); diff --git a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java index a137c4e..c9d5af1 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java @@ -99,7 +99,7 @@ public class AuthCommand { SimpleAuth.config.save(new File("./mods/SimpleAuth/config.json")); if(sender != null) - sender.sendSystemMessage(globalPasswordSet); + ((PlayerEntity) sender).sendMessage(globalPasswordSet, false); else LOGGER.info(SimpleAuth.config.lang.globalPasswordSet); return 1; diff --git a/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java b/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java index f8efcec..ef9ad9f 100644 --- a/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java +++ b/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java @@ -1,10 +1,12 @@ package org.samo_lego.simpleauth.event; +import com.mojang.authlib.GameProfile; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; +import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; @@ -14,6 +16,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.samo_lego.simpleauth.storage.PlayerCache; +import java.net.SocketAddress; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -35,18 +38,40 @@ public class AuthEventHandler { private static Text successfulPortalRescue = new LiteralText(config.lang.successfulPortalRescue); - // Player joining the server - public static void onPlayerJoin(ServerPlayerEntity player) { + // Player pre-join + // Returns text as a reason for disconnect or null to pass + public static LiteralText checkCanPlayerJoinServer(SocketAddress socketAddress, GameProfile profile, PlayerManager manager) { + // Getting the player + String incomingPlayerUsername = profile.getName(); + PlayerEntity onlinePlayer = manager.getPlayer(incomingPlayerUsername); + // Checking if player username is valid String regex = config.main.usernameRegex; Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(player.getName().getString()); - if (!matcher.matches()) { - player.networkHandler.disconnect(new LiteralText(String.format(config.lang.disallowedUsername, regex))); - return; - } + Matcher matcher = pattern.matcher(incomingPlayerUsername); + if(onlinePlayer != null && config.experimental.disableAnotherLocationKick) { + // Player needs to be kicked, since there's already a player with that name + // playing on the server + return new LiteralText( + String.format( + config.lang.playerAlreadyOnline, onlinePlayer.getName().asString() + ) + ); + } + else if(!matcher.matches()) { + return new LiteralText( + String.format( + config.lang.disallowedUsername, regex + ) + ); + } + return null; + } + + // Player joining the server + public static void onPlayerJoin(ServerPlayerEntity player) { // Checking if session is still valid String uuid = player.getUuidAsString(); PlayerCache playerCache = deauthenticatedUsers.getOrDefault(uuid, null); @@ -143,8 +168,8 @@ public class AuthEventHandler { // Setting that player was actually authenticated before leaving PlayerCache playerCache = deauthenticatedUsers.get(player.getUuidAsString()); playerCache.wasAuthenticated = true; + // Setting the session expire time playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000; - } // Player chatting diff --git a/src/main/java/org/samo_lego/simpleauth/event/entity/player/PrePlayerJoinCallback.java b/src/main/java/org/samo_lego/simpleauth/event/entity/player/PrePlayerJoinCallback.java new file mode 100644 index 0000000..bedc3b0 --- /dev/null +++ b/src/main/java/org/samo_lego/simpleauth/event/entity/player/PrePlayerJoinCallback.java @@ -0,0 +1,28 @@ +package org.samo_lego.simpleauth.event.entity.player; + +import com.mojang.authlib.GameProfile; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.PlayerManager; +import net.minecraft.text.LiteralText; + +import java.net.SocketAddress; + +public interface PrePlayerJoinCallback { + Event EVENT = EventFactory.createArrayBacked( + PrePlayerJoinCallback.class, listeners -> ( + socketAddress, profile, manager + ) -> { + for (PrePlayerJoinCallback event : listeners) { + + LiteralText returnText = event.checkCanPlayerJoinServer(socketAddress, profile, manager); + + if (returnText != null) { + return returnText; + } + } + return null; + }); + + LiteralText checkCanPlayerJoinServer(SocketAddress socketAddress, GameProfile profile, PlayerManager manager); +} \ No newline at end of file diff --git a/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerManager.java b/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerManager.java index 37db39a..ba9381f 100644 --- a/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerManager.java +++ b/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerManager.java @@ -1,14 +1,24 @@ package org.samo_lego.simpleauth.mixin; +import com.mojang.authlib.GameProfile; import net.minecraft.network.ClientConnection; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; import org.samo_lego.simpleauth.event.entity.player.PlayerJoinServerCallback; import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback; +import org.samo_lego.simpleauth.event.entity.player.PrePlayerJoinCallback; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.net.SocketAddress; @Mixin(PlayerManager.class) public abstract class MixinPlayerManager { @@ -22,4 +32,18 @@ public abstract class MixinPlayerManager { private void onPlayerLeave(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) { PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity); } + + // Method for kicking player for + @Inject(method = "checkCanJoin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/text/Text;", at = @At("HEAD"), cancellable = true) + private void checkCanJoin(SocketAddress socketAddress, GameProfile profile, CallbackInfoReturnable cir) { + // Getting the player that is trying to join the server + PlayerManager manager = (PlayerManager) (Object) this; + + LiteralText returnText = PrePlayerJoinCallback.EVENT.invoker().checkCanPlayerJoinServer(socketAddress, profile, manager); + + if(returnText != null) { + // Canceling player joining with the returnText message + cir.setReturnValue(returnText); + } + } } diff --git a/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerLoginNetworkHandler.java b/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerLoginNetworkHandler.java deleted file mode 100644 index a56ecd3..0000000 --- a/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerLoginNetworkHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.samo_lego.simpleauth.mixin; - -import com.mojang.authlib.GameProfile; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.text.LiteralText; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import static org.samo_lego.simpleauth.SimpleAuth.config; - - -@Mixin(ServerLoginNetworkHandler.class) -public abstract class MixinServerLoginNetworkHandler { - - @Shadow @Final - private MinecraftServer server; - - @Shadow - private GameProfile profile; - - @Inject(method = "acceptPlayer()V", at = @At("HEAD"), cancellable = true) - private void acceptPlayer(CallbackInfo ci) { - // Player pre-join event, we don't do standard callback, since - // there are lots of variables that would need to be passed over - PlayerEntity onlinePlayer = this.server.getPlayerManager().getPlayer(this.profile.getName()); - - // Getting network handler - ServerLoginNetworkHandler handler = (ServerLoginNetworkHandler) (Object) this; - - if (config.experimental.disableAnotherLocationKick && onlinePlayer != null) { - // Player needs to be kicked, since there's already a player with that name - // playing on the server - handler.disconnect(new LiteralText(String.format(config.lang.playerAlreadyOnline, onlinePlayer.getName().asString()))); - ci.cancel(); - } - } -} diff --git a/src/main/java/org/samo_lego/simpleauth/storage/SimpleAuthDatabase.java b/src/main/java/org/samo_lego/simpleauth/storage/SimpleAuthDatabase.java index c90f2a6..c7062b2 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/SimpleAuthDatabase.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/SimpleAuthDatabase.java @@ -39,6 +39,12 @@ public class SimpleAuthDatabase { } } + // Tells whether db connection is closed + public boolean isClosed() { + return levelDBStore != null; + } + + // When player registers, we insert the data into DB public boolean registerUser(String uuid, String data) { try { diff --git a/src/main/resources/mixins.simpleauth.json b/src/main/resources/mixins.simpleauth.json index 8c5a629..f0db7e1 100644 --- a/src/main/resources/mixins.simpleauth.json +++ b/src/main/resources/mixins.simpleauth.json @@ -7,7 +7,6 @@ "server": [ "MixinPlayerEntity", "MixinPlayerManager", - "MixinServerLoginNetworkHandler", "MixinServerPlayNetworkHandler", "MixinSlot" ],