From 75461b37ddca33527688e08c81d29e0742e2ab08 Mon Sep 17 00:00:00 2001 From: samo_lego <34912839+samolego@users.noreply.github.com> Date: Thu, 15 Oct 2020 21:34:46 +0200 Subject: [PATCH] Biggest refactor yet --- .../org/samo_lego/simpleauth/SimpleAuth.java | 196 +---------------- .../simpleauth/commands/AccountCommand.java | 20 +- .../simpleauth/commands/LoginCommand.java | 9 +- .../simpleauth/commands/LogoutCommand.java | 13 +- .../simpleauth/commands/RegisterCommand.java | 10 +- .../simpleauth/event/AuthEventHandler.java | 80 ++++--- .../simpleauth/mixin/MixinPlayerEntity.java | 200 +++++++++++++++++- .../simpleauth/mixin/MixinPlayerManager.java | 2 +- .../simpleauth/storage/PlayerCache.java | 47 ++-- .../simpleauth/utils/PlayerAuth.java | 16 ++ .../simpleauth/utils/UuidConverter.java | 33 --- 11 files changed, 309 insertions(+), 317 deletions(-) create mode 100644 src/main/java/org/samo_lego/simpleauth/utils/PlayerAuth.java delete mode 100644 src/main/java/org/samo_lego/simpleauth/utils/UuidConverter.java diff --git a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java index 004a859..072adb6 100644 --- a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java +++ b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java @@ -6,16 +6,7 @@ import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.player.*; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.LiteralText; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.registry.Registry; -import net.minecraft.util.registry.RegistryKey; -import net.minecraft.world.World; import org.iq80.leveldb.WriteBatch; import org.samo_lego.simpleauth.commands.*; import org.samo_lego.simpleauth.event.AuthEventHandler; @@ -32,8 +23,6 @@ import java.io.IOException; import java.nio.file.Path; import java.util.HashMap; import java.util.Properties; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -41,10 +30,8 @@ import java.util.concurrent.TimeUnit; import static org.iq80.leveldb.impl.Iq80DBFactory.bytes; import static org.samo_lego.simpleauth.commands.AuthCommand.reloadConfig; import static org.samo_lego.simpleauth.event.AuthEventHandler.*; -import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake; import static org.samo_lego.simpleauth.utils.SimpleLogger.logError; import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; public class SimpleAuth implements DedicatedServerModInitializer { @@ -52,27 +39,13 @@ public class SimpleAuth implements DedicatedServerModInitializer { public static final ExecutorService THREADPOOL = Executors.newCachedThreadPool(); - private static final Timer TIMER = new Timer(); - /** * HashMap of players that have joined the server. * It's cleared on server stop in order to save some interactions with database during runtime. - * Stores their data as {@link org.samo_lego.simpleauth.storage.PlayerCache PlayerCache} object + * Stores their data as {@link org.samo_lego.simpleauth.storage.PlayerCache PlayerCache} object. */ public static HashMap playerCacheMap = new HashMap<>(); - /** - * Checks whether player is authenticated. - * Fake players always count as authenticated. - * - * @param player player that needs to be checked - * @return false if player is not authenticated, otherwise true - */ - public static boolean isAuthenticated(ServerPlayerEntity player) { - String uuid = convertUuid(player); - return playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated; - } - // Getting game directory public static final Path gameDirectory = FabricLoader.getInstance().getGameDir(); @@ -145,14 +118,6 @@ public class SimpleAuth implements DedicatedServerModInitializer { JsonObject data = new JsonObject(); data.addProperty("password", playerCache.password); - JsonObject lastLocation = new JsonObject(); - lastLocation.addProperty("dim", playerCache.lastLocation.lastDim); - lastLocation.addProperty("x", playerCache.lastLocation.lastX); - lastLocation.addProperty("y", playerCache.lastLocation.lastY); - lastLocation.addProperty("z", playerCache.lastLocation.lastZ); - - data.addProperty("lastLocation", lastLocation.toString()); - batch.put(bytes("UUID:" + uuid), bytes("data:" + data.toString())); }); try { @@ -166,8 +131,6 @@ public class SimpleAuth implements DedicatedServerModInitializer { // Closing threads try { THREADPOOL.shutdownNow(); - TIMER.cancel(); - TIMER.purge(); if (!THREADPOOL.awaitTermination(100, TimeUnit.MICROSECONDS)) { Thread.currentThread().interrupt(); } @@ -179,161 +142,4 @@ public class SimpleAuth implements DedicatedServerModInitializer { // Closing DB connection DB.close(); } - - /** - * Gets the text which tells the player - * to login or register, depending on account status. - * - * @param player player who will get the message - * @return LiteralText with appropriate string (login or register) - */ - public static LiteralText notAuthenticated(PlayerEntity player) { - final PlayerCache cache = playerCacheMap.get(convertUuid(player)); - if(SimpleAuth.config.main.enableGlobalPassword || cache.isRegistered) - return new LiteralText( - SimpleAuth.config.lang.notAuthenticated + "\n" + SimpleAuth.config.lang.loginRequired - ); - return new LiteralText( - SimpleAuth.config.lang.notAuthenticated+ "\n" + SimpleAuth.config.lang.registerRequired - ); - } - - /** - * Authenticates player and sends the success message. - * - * @param player player that needs to be authenticated - * @param msg message to be send to the player - */ - public static void authenticatePlayer(ServerPlayerEntity player, Text msg) { - PlayerCache playerCache = playerCacheMap.get(convertUuid(player)); - // Teleporting player back - if(config.main.spawnOnJoin) - teleportPlayer(player, false); - - // Updating blocks if needed (if portal rescue action happened) - if(playerCache.wasInPortal) { - World world = player.getEntityWorld(); - BlockPos pos = player.getBlockPos(); - - // Sending updates to portal blocks - // This is technically not needed, but it cleans the "messed portal" on the client - world.updateListeners(pos, world.getBlockState(pos), world.getBlockState(pos), 3); - world.updateListeners(pos.up(), world.getBlockState(pos.up()), world.getBlockState(pos.up()), 3); - } - - // Setting last air to player - if(player.isSubmergedInWater()) - player.setAir(playerCache.lastAir); - - // In case player is in lava during authentication proccess - if(!playerCache.wasOnFire) - player.setFireTicks(0); - - playerCache.isAuthenticated = true; - - // Player no longer needs to be invisible and invulnerable - player.setInvulnerable(false); - player.setInvisible(false); - player.sendMessage(msg, false); - } - - /** - * De-authenticates the player. - * - * @param player player that needs to be de-authenticated - */ - public static void deauthenticatePlayer(ServerPlayerEntity player) { - if(DB.isClosed()) - return; - - // Marking player as not authenticated - String uuid = convertUuid(player); - PlayerCache cache = playerCacheMap.get(uuid); - if(cache == null) { - cache = new PlayerCache(uuid, player); - playerCacheMap.put(uuid, cache); - } - cache.isAuthenticated = false; - - - // Teleporting player to spawn to hide its position - if(config.main.spawnOnJoin) - teleportPlayer(player, true); - - // Player is now not authenticated - player.sendMessage(notAuthenticated(player), false); - - // Setting the player to be invisible to mobs and also invulnerable - player.setInvulnerable(SimpleAuth.config.experimental.playerInvulnerable); - player.setInvisible(SimpleAuth.config.experimental.playerInvisible); - - // Kicks player after a certain amount of time if not authenticated - TIMER.schedule(new TimerTask() { - @Override - public void run() { - // Kicking player if not authenticated - if(!isAuthenticated(player) && player.networkHandler.getConnection().isOpen()) - player.networkHandler.disconnect(new LiteralText(SimpleAuth.config.lang.timeExpired)); - } - }, SimpleAuth.config.main.kickTime * 1000); - } - - /** - * Checks whether player is a fake player (from CarpetMod). - * - * @param player player that needs to be checked - * @return true if player is fake, otherwise false - */ - public static boolean isPlayerFake(PlayerEntity player) { - // We ask CarpetHelper class since it has the imports needed - return FabricLoader.getInstance().isModLoaded("carpet") && isPlayerCarpetFake(player); - } - - /** - * Teleports player to spawn or last location that is recorded. - * Last location means the location before de-authentication. - * - * @param player player that needs to be teleported - * @param toSpawn whether to teleport player to spawn (provided in config) or last recorded position - */ - public static void teleportPlayer(ServerPlayerEntity player, boolean toSpawn) { - MinecraftServer server = player.getServer(); - if(server == null || config.worldSpawn.dimension == null) - return; - - if (toSpawn) { - // Teleports player to spawn - player.teleport( - server.getWorld(RegistryKey.of(Registry.DIMENSION, new Identifier(config.worldSpawn.dimension))), - config.worldSpawn.x, - config.worldSpawn.y, - config.worldSpawn.z, - config.worldSpawn.yaw, - config.worldSpawn.pitch - ); - return; - } - PlayerCache cache = playerCacheMap.get(convertUuid(player)); - // Puts player to last cached position - try { - player.teleport( - server.getWorld(RegistryKey.of(Registry.DIMENSION, new Identifier(cache.lastLocation.lastDim))), - cache.lastLocation.lastX, - cache.lastLocation.lastY, - cache.lastLocation.lastZ, - cache.lastLocation.lastYaw, - cache.lastLocation.lastPitch - ); - } catch (Error e) { - player.sendMessage(new LiteralText(config.lang.corruptedPlayerData), false); - logError("Couldn't teleport player " + player.getName().asString()); - logError( - String.format("Last recorded position is X: %s, Y: %s, Z: %s in dimension %s", - cache.lastLocation.lastX, - cache.lastLocation.lastY, - cache.lastLocation.lastZ, - cache.lastLocation.lastDim - )); - } - } } diff --git a/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java index ae21638..37b1903 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/AccountCommand.java @@ -5,15 +5,14 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; -import org.samo_lego.simpleauth.SimpleAuth; import org.samo_lego.simpleauth.utils.AuthHelper; +import org.samo_lego.simpleauth.utils.PlayerAuth; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.word; import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; import static org.samo_lego.simpleauth.SimpleAuth.*; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; public class AccountCommand { @@ -36,7 +35,7 @@ public class AccountCommand { ) ) ) - .then(literal("changePassword") + .then(literal("changePassword") //todo mongodb update .then(argument("old password", word()) .executes(ctx -> { ctx.getSource().getPlayer().sendMessage( @@ -72,13 +71,10 @@ public class AccountCommand { // Different thread to avoid lag spikes THREADPOOL.submit(() -> { - if (AuthHelper.checkPassword(convertUuid(player), pass.toCharArray()) == 1) { - DB.deleteUserData(convertUuid(player)); - player.sendMessage( - new LiteralText(config.lang.accountDeleted), - false - ); - SimpleAuth.deauthenticatePlayer(player); + if (AuthHelper.checkPassword(((PlayerAuth) player).getFakeUuid(), pass.toCharArray()) == 1) { + DB.deleteUserData(((PlayerAuth) player).getFakeUuid()); + player.sendMessage(new LiteralText(config.lang.accountDeleted), false); + ((PlayerAuth) player).setAuthenticated(false); return; } player.sendMessage( @@ -103,7 +99,7 @@ public class AccountCommand { } // Different thread to avoid lag spikes THREADPOOL.submit(() -> { - if (AuthHelper.checkPassword(convertUuid(player), oldPass.toCharArray()) == 1) { + if (AuthHelper.checkPassword(((PlayerAuth) player).getFakeUuid(), oldPass.toCharArray()) == 1) { if (newPass.length() < config.main.minPasswordChars) { player.sendMessage(new LiteralText( String.format(config.lang.minPasswordChars, config.main.minPasswordChars) @@ -117,7 +113,7 @@ public class AccountCommand { return; } // Changing password in playercache - playerCacheMap.get(convertUuid(player)).password = AuthHelper.hashPassword(newPass.toCharArray()); + playerCacheMap.get(((PlayerAuth) player).getFakeUuid()).password = AuthHelper.hashPassword(newPass.toCharArray()); player.sendMessage( new LiteralText(config.lang.passwordUpdated), false diff --git a/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java index b28c570..3374865 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/LoginCommand.java @@ -6,13 +6,13 @@ import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import org.samo_lego.simpleauth.utils.AuthHelper; +import org.samo_lego.simpleauth.utils.PlayerAuth; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.word; import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; import static org.samo_lego.simpleauth.SimpleAuth.*; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; public class LoginCommand { @@ -32,8 +32,8 @@ public class LoginCommand { private static int login(ServerCommandSource source, String pass) throws CommandSyntaxException { // Getting the player who send the command ServerPlayerEntity player = source.getPlayer(); - String uuid = convertUuid(player); - if (isAuthenticated(player)) { + String uuid = ((PlayerAuth) player).getFakeUuid(); + if (((PlayerAuth) player).isAuthenticated()) { player.sendMessage(new LiteralText(config.lang.alreadyAuthenticated), false); return 0; } @@ -47,7 +47,8 @@ public class LoginCommand { return; } else if(passwordResult == 1) { - authenticatePlayer(player, new LiteralText(config.lang.successfullyAuthenticated)); + player.sendMessage(new LiteralText(config.lang.successfullyAuthenticated), false); + ((PlayerAuth) player).setAuthenticated(true); return; } else if(passwordResult == -1) { diff --git a/src/main/java/org/samo_lego/simpleauth/commands/LogoutCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/LogoutCommand.java index 1ffd8ee..f0b5665 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/LogoutCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/LogoutCommand.java @@ -6,10 +6,11 @@ import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import org.samo_lego.simpleauth.storage.PlayerCache; +import org.samo_lego.simpleauth.utils.PlayerAuth; import static net.minecraft.server.command.CommandManager.literal; -import static org.samo_lego.simpleauth.SimpleAuth.*; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; +import static org.samo_lego.simpleauth.SimpleAuth.config; +import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap; public class LogoutCommand { @@ -22,15 +23,15 @@ public class LogoutCommand { private static int logout(ServerCommandSource serverCommandSource) throws CommandSyntaxException { ServerPlayerEntity player = serverCommandSource.getPlayer(); - PlayerCache playerCache = playerCacheMap.get(convertUuid(player)); + PlayerCache playerCache = playerCacheMap.get(((PlayerAuth) player).getFakeUuid()); playerCache.lastLocation.lastDim = String.valueOf(player.getEntityWorld().getRegistryKey().getValue()); playerCache.lastLocation.lastX = player.getX(); playerCache.lastLocation.lastY = player.getY(); playerCache.lastLocation.lastZ = player.getZ(); - playerCache.lastLocation.lastYaw = player.yaw; - playerCache.lastLocation.lastPitch = player.pitch; + playerCache.lastLocation.yaw = player.yaw; + playerCache.lastLocation.pitch = player.pitch; - deauthenticatePlayer(player); + ((PlayerAuth) player).setAuthenticated(false); player.sendMessage(new LiteralText(config.lang.successfulLogout), false); return 1; } diff --git a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java index fede8ea..dd293c9 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/RegisterCommand.java @@ -6,6 +6,7 @@ import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.LiteralText; import org.samo_lego.simpleauth.storage.PlayerCache; +import org.samo_lego.simpleauth.utils.PlayerAuth; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.word; @@ -13,7 +14,6 @@ import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; import static org.samo_lego.simpleauth.SimpleAuth.*; import static org.samo_lego.simpleauth.utils.AuthHelper.hashPassword; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; public class RegisterCommand { @@ -39,7 +39,7 @@ public class RegisterCommand { player.sendMessage(new LiteralText(config.lang.loginRequired), false); return 0; } - else if(isAuthenticated(player)) { + else if(((PlayerAuth) player).isAuthenticated()) { player.sendMessage(new LiteralText(config.lang.alreadyAuthenticated), false); return 0; } @@ -62,9 +62,11 @@ public class RegisterCommand { return; } - PlayerCache playerCache = playerCacheMap.get(convertUuid(player)); + PlayerCache playerCache = playerCacheMap.get(((PlayerAuth) player).getFakeUuid()); if (!playerCache.isRegistered) { - authenticatePlayer(player, new LiteralText(config.lang.registerSuccess)); + ((PlayerAuth) player).setAuthenticated(true); + player.sendMessage(new LiteralText(config.lang.registerSuccess), false); + playerCache.password = hashPassword(pass1.toCharArray()); playerCache.isRegistered = true; return; 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 d02ecdf..a20be4e 100644 --- a/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java +++ b/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java @@ -13,13 +13,14 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.TypedActionResult; import net.minecraft.util.math.BlockPos; import org.samo_lego.simpleauth.storage.PlayerCache; +import org.samo_lego.simpleauth.utils.PlayerAuth; import java.net.SocketAddress; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.samo_lego.simpleauth.SimpleAuth.*; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; +import static org.samo_lego.simpleauth.SimpleAuth.config; +import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap; /** * This class will take care of actions players try to do, @@ -40,7 +41,7 @@ public class AuthEventHandler { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(incomingPlayerUsername); - if((onlinePlayer != null && !isPlayerFake(onlinePlayer)) && config.experimental.preventAnotherLocationKick) { + if((onlinePlayer != null && !((PlayerAuth) onlinePlayer).canSkipAuth()) && config.experimental.preventAnotherLocationKick) { // Player needs to be kicked, since there's already a player with that name // playing on the server return new LiteralText( @@ -63,10 +64,10 @@ public class AuthEventHandler { // Player joining the server public static void onPlayerJoin(ServerPlayerEntity player) { // If player is fake auth is not needed - if (isPlayerFake(player)) + if (((PlayerAuth) player).canSkipAuth()) return; // Checking if session is still valid - String uuid = convertUuid(player); + String uuid = ((PlayerAuth) player).getFakeUuid(); PlayerCache playerCache = playerCacheMap.getOrDefault(uuid, null); if (playerCache != null) { @@ -75,7 +76,7 @@ public class AuthEventHandler { playerCache.validUntil >= System.currentTimeMillis() && player.getIp().equals(playerCache.lastIp) ) { - authenticatePlayer(player, null); // Makes player authenticated + ((PlayerAuth) player).setAuthenticated(true); return; } player.setInvulnerable(config.experimental.playerInvulnerable); @@ -83,17 +84,15 @@ public class AuthEventHandler { // Invalidating session playerCache.isAuthenticated = false; - player.sendMessage(notAuthenticated(player), false); + if(config.main.spawnOnJoin) + ((PlayerAuth) player).hidePosition(true); } else { - deauthenticatePlayer(player); + ((PlayerAuth) player).setAuthenticated(false); playerCache = playerCacheMap.get(uuid); playerCache.wasOnFire = false; } - if(config.main.spawnOnJoin) - teleportPlayer(player, true); - // Tries to rescue player from nether portal if(config.main.tryPortalRescue && player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL)) { @@ -112,27 +111,24 @@ public class AuthEventHandler { } public static void onPlayerLeave(ServerPlayerEntity player) { - if(isPlayerFake(player)) + if(((PlayerAuth) player).canSkipAuth()) return; - String uuid = convertUuid(player); + String uuid = ((PlayerAuth) player).getFakeUuid(); PlayerCache playerCache = playerCacheMap.get(uuid); - playerCache.lastIp = player.getIp(); - playerCache.lastAir = player.getAir(); - playerCache.wasOnFire = player.isOnFire(); - playerCache.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL); - if(isAuthenticated(player)) { - playerCache.lastLocation.lastDim = String.valueOf(player.getEntityWorld().getRegistryKey().getValue()); - playerCache.lastLocation.lastX = player.getX(); - playerCache.lastLocation.lastY = player.getY(); - playerCache.lastLocation.lastZ = player.getZ(); - playerCache.lastLocation.lastYaw = player.yaw; - playerCache.lastLocation.lastPitch = player.pitch; - + if(((PlayerAuth) player).isAuthenticated()) { + playerCache.lastIp = player.getIp(); + playerCache.lastAir = player.getAir(); + playerCache.wasOnFire = player.isOnFire(); + playerCache.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL); + // Setting the session expire time if(config.main.sessionTimeoutTime != -1) playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000; } + else { + ((PlayerAuth) player).hidePosition(false); + } } // Player chatting @@ -140,12 +136,12 @@ public class AuthEventHandler { // Getting the message to then be able to check it String msg = chatMessageC2SPacket.getChatMessage(); if( - !isAuthenticated((ServerPlayerEntity) player) && + !((PlayerAuth) player).isAuthenticated() && !msg.startsWith("/login") && !msg.startsWith("/register") && (!config.experimental.allowChat || msg.startsWith("/")) ) { - player.sendMessage(notAuthenticated(player), false); + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return ActionResult.FAIL; } return ActionResult.PASS; @@ -154,7 +150,7 @@ public class AuthEventHandler { // Player movement public static ActionResult onPlayerMove(PlayerEntity player) { // Player will fall if enabled (prevent fly kick) - boolean auth = isAuthenticated((ServerPlayerEntity) player); + boolean auth = ((PlayerAuth) player).isAuthenticated(); if(!auth && config.main.allowFalling && !player.isOnGround() && !player.isInsideWaterOrBubbleColumn()) { if(player.isInvulnerable()) player.setInvulnerable(false); @@ -171,8 +167,8 @@ public class AuthEventHandler { // Using a block (right-click function) public static ActionResult onUseBlock(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowBlockUse) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowBlockUse) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return ActionResult.FAIL; } return ActionResult.PASS; @@ -180,8 +176,8 @@ public class AuthEventHandler { // Breaking a block public static boolean onBreakBlock(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowBlockPunch) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowBlockPunch) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return false; } return true; @@ -189,8 +185,8 @@ public class AuthEventHandler { // Using an item public static TypedActionResult onUseItem(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowItemUse) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowItemUse) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return TypedActionResult.fail(ItemStack.EMPTY); } @@ -198,16 +194,16 @@ public class AuthEventHandler { } // Dropping an item public static ActionResult onDropItem(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowItemDrop) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowItemDrop) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return ActionResult.FAIL; } return ActionResult.PASS; } // Changing inventory (item moving etc.) public static ActionResult onTakeItem(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowItemMoving) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowItemMoving) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return ActionResult.FAIL; } @@ -215,8 +211,8 @@ public class AuthEventHandler { } // Attacking an entity public static ActionResult onAttackEntity(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowEntityPunch) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowEntityPunch) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return ActionResult.FAIL; } @@ -224,8 +220,8 @@ public class AuthEventHandler { } // Interacting with entity public static ActionResult onUseEntity(PlayerEntity player) { - if(!isAuthenticated((ServerPlayerEntity) player) && !config.main.allowEntityInteract) { - player.sendMessage(notAuthenticated(player), false); + if(!((PlayerAuth) player).isAuthenticated() && !config.main.allowEntityInteract) { + player.sendMessage(((PlayerAuth) player).getAuthMessage(), false); return ActionResult.FAIL; } diff --git a/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.java b/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.java index 731f908..c5e3944 100644 --- a/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.java +++ b/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.java @@ -1,17 +1,215 @@ package org.samo_lego.simpleauth.mixin; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; +import net.minecraft.world.World; +import org.samo_lego.simpleauth.SimpleAuth; import org.samo_lego.simpleauth.event.item.DropItemCallback; +import org.samo_lego.simpleauth.storage.PlayerCache; +import org.samo_lego.simpleauth.utils.PlayerAuth; import org.spongepowered.asm.mixin.Mixin; 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 static org.samo_lego.simpleauth.SimpleAuth.config; +import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap; +import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake; + @Mixin(PlayerEntity.class) -public abstract class MixinPlayerEntity { +public abstract class MixinPlayerEntity implements PlayerAuth { + + private final ServerPlayerEntity player = (ServerPlayerEntity) (Object) this; + + // * 20 for 20 ticks in second + private int kickTimer = config.main.kickTime * 20; + + private final boolean isRunningCarpet = FabricLoader.getInstance().isModLoaded("carpet"); + + private final MinecraftServer server = player.getServer(); + + /** + * Teleports player to spawn or last location that is recorded. + * Last location means the location before de-authentication. + * + * @param hide whether to teleport player to spawn (provided in config) or last recorded position + */ + @Override + public void hidePosition(boolean hide) { + assert server != null; + + PlayerCache cache = playerCacheMap.get(this.getFakeUuid()); + System.out.println("Teleporting Player. hide:" + hide); + if (hide) { + // Saving position + cache.lastLocation.dimension = player.getServerWorld(); + cache.lastLocation.position = player.getPos(); + cache.lastLocation.yaw = player.yaw; + cache.lastLocation.pitch = player.pitch; + + // Teleports player to spawn + player.teleport( + server.getWorld(RegistryKey.of(Registry.DIMENSION, new Identifier(config.worldSpawn.dimension))), + config.worldSpawn.x, + config.worldSpawn.y, + config.worldSpawn.z, + config.worldSpawn.yaw, + config.worldSpawn.pitch + ); + return; + } + // Puts player to last cached position + player.teleport( + cache.lastLocation.dimension, + cache.lastLocation.position.getX(), + cache.lastLocation.position.getY(), + cache.lastLocation.position.getZ(), + cache.lastLocation.yaw, + cache.lastLocation.pitch + ); + } + + /** + * Converts player uuid, to ensure player with "nAmE" and "NamE" get same uuid. + * Both players are not allowed to play, since mod mimics Mojang behaviour. + * of not allowing accounts with same names but different capitalization. + * + * @return converted UUID as string + */ + @Override + public String getFakeUuid() { + // If server is in online mode online-mode UUIDs should be used + assert server != null; + if(server.isOnlineMode()) + return player.getUuidAsString(); + /* + Lower case is used for Player and PlAyEr to get same UUID (for password storing) + Mimicking Mojang behaviour, where players cannot set their name to + ExAmple if Example is already taken. + */ + String playername = player.getName().asString().toLowerCase(); + return PlayerEntity.getOfflinePlayerUuid(playername).toString(); + + } + + /** + * Sets the authentication status of the player. + * + * @param authenticated whether player should be authenticated + */ + @Override + public void setAuthenticated(boolean authenticated) { + PlayerCache playerCache; + + if(!playerCacheMap.containsKey(this.getFakeUuid())) { + // First join + playerCache = new PlayerCache(this.getFakeUuid(), player); + playerCacheMap.put(this.getFakeUuid(), playerCache); + } + else { + playerCache = playerCacheMap.get(this.getFakeUuid()); + if(this.isAuthenticated() == authenticated) + return; + playerCache.isAuthenticated = authenticated; + } + + player.setInvulnerable(!authenticated && config.experimental.playerInvulnerable); + player.setInvisible(!authenticated && config.experimental.playerInvisible); + + // Teleporting player (hiding / restoring position) + if(config.main.spawnOnJoin) + this.hidePosition(!authenticated); + + if(authenticated) { + kickTimer = config.main.kickTime * 20; + // Updating blocks if needed (if portal rescue action happened) + if(playerCache.wasInPortal) { + World world = player.getEntityWorld(); + BlockPos pos = player.getBlockPos(); + + // Sending updates to portal blocks + // This is technically not needed, but it cleans the "messed portal" on the client + world.updateListeners(pos, world.getBlockState(pos), world.getBlockState(pos), 3); + world.updateListeners(pos.up(), world.getBlockState(pos.up()), world.getBlockState(pos.up()), 3); + } + + // Setting last air to player + if(player.isSubmergedInWater()) + player.setAir(playerCache.lastAir); + + // In case player is in lava during authentication proccess + if(!playerCache.wasOnFire) + player.setFireTicks(0); + } + } + + /** + * Gets the text which tells the player + * to login or register, depending on account status. + * + * @return LiteralText with appropriate string (login or register) + */ + @Override + public Text getAuthMessage() { + final PlayerCache cache = playerCacheMap.get(((PlayerAuth) player).getFakeUuid()); + if(SimpleAuth.config.main.enableGlobalPassword || cache.isRegistered) + return new LiteralText( + SimpleAuth.config.lang.notAuthenticated + "\n" + SimpleAuth.config.lang.loginRequired + ); + return new LiteralText( + SimpleAuth.config.lang.notAuthenticated+ "\n" + SimpleAuth.config.lang.registerRequired + ); + } + + /** + * Checks whether player is a fake player (from CarpetMod). + * + * @return true if player is fake (can skip authentication process), otherwise false + */ + @Override + public boolean canSkipAuth() { + // We ask CarpetHelper class since it has the imports needed + return this.isRunningCarpet && isPlayerCarpetFake(this.player); + } + + /** + * Checks whether player is authenticated. + * + * @return false if player is not authenticated, otherwise true. + */ + @Override + public boolean isAuthenticated() { + String uuid = ((PlayerAuth) player).getFakeUuid(); + return playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated; + } + + @Inject(method = "tick()V", at = @At("HEAD")) + private void tick(CallbackInfo ci) { + if(!this.isAuthenticated()) { + // Checking player timer + if(kickTimer <= 0 && player.networkHandler.getConnection().isOpen()) { + player.networkHandler.disconnect(new LiteralText(config.lang.timeExpired)); + } + else { + // Sending authentication prompt every 10 seconds + if(kickTimer % 200 == 0) + player.sendMessage(this.getAuthMessage(), false); + kickTimer--; + } + } + } + // Player item dropping @Inject(method = "dropSelectedItem(Z)Z", at = @At("HEAD"), cancellable = true) private void dropSelectedItem(boolean dropEntireStack, CallbackInfoReturnable cir) { 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 0a48656..d0ae290 100644 --- a/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerManager.java +++ b/src/main/java/org/samo_lego/simpleauth/mixin/MixinPlayerManager.java @@ -25,7 +25,7 @@ public abstract class MixinPlayerManager { PlayerJoinServerCallback.EVENT.invoker().onPlayerJoin(serverPlayerEntity); } - @Inject(method = "remove(Lnet/minecraft/server/network/ServerPlayerEntity;)V", at = @At("RETURN")) + @Inject(method = "remove(Lnet/minecraft/server/network/ServerPlayerEntity;)V", at = @At("HEAD")) private void onPlayerLeave(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) { PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity); } diff --git a/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java b/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java index 7880644..c814558 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java @@ -3,7 +3,14 @@ package org.samo_lego.simpleauth.storage; import com.google.gson.*; import net.minecraft.block.Blocks; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.util.registry.RegistryKey; + +import java.util.Objects; import static org.samo_lego.simpleauth.SimpleAuth.DB; import static org.samo_lego.simpleauth.SimpleAuth.config; @@ -51,12 +58,10 @@ public class PlayerCache { * Last recorded position before de-authentication. */ public static class LastLocation { - public String lastDim; - public double lastX; - public double lastY; - public double lastZ; - public float lastYaw; - public float lastPitch; + public ServerWorld dimension; + public Vec3d position; + public float yaw; + public float pitch; } public PlayerCache.LastLocation lastLocation = new PlayerCache.LastLocation(); @@ -77,13 +82,12 @@ public class PlayerCache { this.wasOnFire = player.isOnFire(); // Setting position cache - this.lastLocation.lastDim = String.valueOf(player.getEntityWorld().getRegistryKey().getValue()); + this.lastLocation.dimension = player.getServerWorld(); + this.lastLocation.position = player.getPos(); + this.lastLocation.yaw = player.yaw; + this.lastLocation.pitch = player.pitch; + this.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL); - this.lastLocation.lastX = player.getX(); - this.lastLocation.lastY = player.getY(); - this.lastLocation.lastZ = player.getZ(); - this.lastLocation.lastYaw = player.yaw; - this.lastLocation.lastPitch = player.pitch; } else { this.wasOnFire = false; @@ -112,19 +116,24 @@ public class PlayerCache { } - // We should check the DB for saved coords + // DEPRECATED, UGLY if(config.main.spawnOnJoin) { try { JsonElement lastLoc = json.get("lastLocation"); if (lastLoc != null) { // Getting DB coords JsonObject lastLocation = gson.fromJson(lastLoc.getAsString(), JsonObject.class); - this.lastLocation.lastDim = lastLocation.get("dim").isJsonNull() ? config.worldSpawn.dimension : lastLocation.get("dim").getAsString(); - this.lastLocation.lastX = lastLocation.get("x").isJsonNull() ? config.worldSpawn.x : lastLocation.get("x").getAsDouble(); - this.lastLocation.lastY = lastLocation.get("y").isJsonNull() ? config.worldSpawn.y : lastLocation.get("y").getAsDouble(); - this.lastLocation.lastZ = lastLocation.get("z").isJsonNull() ? config.worldSpawn.z : lastLocation.get("z").getAsDouble(); - this.lastLocation.lastYaw = lastLocation.get("yaw") == null ? 90 : lastLocation.get("yaw").getAsFloat(); - this.lastLocation.lastPitch = lastLocation.get("pitch") == null ? 0 : lastLocation.get("pitch").getAsFloat(); + assert player != null; + this.lastLocation.dimension = Objects.requireNonNull(player.getServer()).getWorld(RegistryKey.of(Registry.DIMENSION, new Identifier( + lastLocation.get("dim").isJsonNull() ? config.worldSpawn.dimension : lastLocation.get("dim").getAsString()))); + + this.lastLocation.position = new Vec3d( + lastLocation.get("x").isJsonNull() ? config.worldSpawn.x : lastLocation.get("x").getAsDouble(), + lastLocation.get("y").isJsonNull() ? config.worldSpawn.y : lastLocation.get("y").getAsDouble(), + lastLocation.get("z").isJsonNull() ? config.worldSpawn.z : lastLocation.get("z").getAsDouble() + ); + this.lastLocation.yaw = lastLocation.get("yaw") == null ? 90 : lastLocation.get("yaw").getAsFloat(); + this.lastLocation.pitch = lastLocation.get("pitch") == null ? 0 : lastLocation.get("pitch").getAsFloat(); } } catch (JsonSyntaxException ignored) { diff --git a/src/main/java/org/samo_lego/simpleauth/utils/PlayerAuth.java b/src/main/java/org/samo_lego/simpleauth/utils/PlayerAuth.java new file mode 100644 index 0000000..1fbf435 --- /dev/null +++ b/src/main/java/org/samo_lego/simpleauth/utils/PlayerAuth.java @@ -0,0 +1,16 @@ +package org.samo_lego.simpleauth.utils; + +import net.minecraft.text.Text; + +public interface PlayerAuth { + void hidePosition(boolean hide); + + String getFakeUuid(); + + void setAuthenticated(boolean authenticated); + boolean isAuthenticated(); + + Text getAuthMessage(); + + boolean canSkipAuth(); +} diff --git a/src/main/java/org/samo_lego/simpleauth/utils/UuidConverter.java b/src/main/java/org/samo_lego/simpleauth/utils/UuidConverter.java deleted file mode 100644 index 8d0aff5..0000000 --- a/src/main/java/org/samo_lego/simpleauth/utils/UuidConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.samo_lego.simpleauth.utils; - -import net.minecraft.entity.player.PlayerEntity; - -import static org.samo_lego.simpleauth.SimpleAuth.serverProp; - -/** - * Converts player uuid, to ensure player with "nAmE" and "NamE" get same uuid - * Both players are not allowed to play, since mod mimics Mojang behaviour - * of not allowing accounts with same names but different capitalization - */ -public class UuidConverter { - - private static final boolean isOnline = Boolean.parseBoolean(serverProp.getProperty("online-mode")); - - /** Gets player UUID. - * - * @param player player to get UUID for - * @return converted UUID as string - */ - public static String convertUuid(PlayerEntity player) { - // If server is in online mode online-mode UUIDs should be used - if(isOnline) - return player.getUuidAsString(); - /* - Lower case is used for Player and PlAyEr to get same UUID (for password storing) - Mimicking Mojang behaviour, where players cannot set their name to - ExAmple if Example is already taken. - */ - String playername = player.getName().asString().toLowerCase(); - return PlayerEntity.getOfflinePlayerUuid(playername).toString(); - } -}