From 8405680f4278914ab596f66cc68c2b4656ff1410 Mon Sep 17 00:00:00 2001 From: samo_lego <34912839+samolego@users.noreply.github.com> Date: Thu, 7 May 2020 21:32:45 +0200 Subject: [PATCH] Finalising position protection. --- .../org/samo_lego/simpleauth/SimpleAuth.java | 31 ++++++++++++ .../simpleauth/commands/AuthCommand.java | 38 +++++++------- .../simpleauth/event/AuthEventHandler.java | 28 +++++++---- .../mixin/MixinServerPlayNetworkHandler.java | 50 ------------------- .../simpleauth/storage/AuthConfig.java | 4 +- .../simpleauth/storage/PlayerCache.java | 48 +++++++++++++++--- .../storage/SimpleAuthDatabase.java | 6 +-- 7 files changed, 111 insertions(+), 94 deletions(-) diff --git a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java index 11947bb..c487f4f 100644 --- a/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java +++ b/src/main/java/org/samo_lego/simpleauth/SimpleAuth.java @@ -1,5 +1,6 @@ package org.samo_lego.simpleauth; +import com.google.gson.JsonObject; import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.event.player.*; import net.fabricmc.fabric.api.event.server.ServerStopCallback; @@ -13,6 +14,7 @@ import net.minecraft.text.Text; import net.minecraft.world.dimension.DimensionType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.iq80.leveldb.WriteBatch; import org.samo_lego.simpleauth.commands.*; import org.samo_lego.simpleauth.event.AuthEventHandler; import org.samo_lego.simpleauth.event.entity.player.*; @@ -23,10 +25,12 @@ import org.samo_lego.simpleauth.storage.PlayerCache; import org.samo_lego.simpleauth.storage.SimpleAuthDatabase; import java.io.File; +import java.io.IOException; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; +import static org.iq80.leveldb.impl.Iq80DBFactory.bytes; import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake; import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; @@ -98,6 +102,32 @@ public class SimpleAuth implements DedicatedServerModInitializer { private static void onStopServer() { LOGGER.info("[SimpleAuth] Shutting down SimpleAuth."); + + WriteBatch batch = db.levelDBStore.createWriteBatch(); + // Writing coords of de-authenticated players to database + deauthenticatedUsers.forEach((uuid, playerCache) -> { + JsonObject data = new JsonObject(); + data.addProperty("password", playerCache.password); + + JsonObject lastLocation = new JsonObject(); + lastLocation.addProperty("dimId", playerCache.lastDimId); + lastLocation.addProperty("x", playerCache.lastX); + lastLocation.addProperty("y", playerCache.lastY); + lastLocation.addProperty("z", playerCache.lastZ); + + data.addProperty("lastLocation", lastLocation.toString()); + + batch.put(bytes("UUID:" + uuid), bytes("data:" + data.toString())); + }); + try { + // Writing and closing batch + db.levelDBStore.write(batch); + batch.close(); + } catch (IOException e) { + LOGGER.error("[SimpleAuth] Error saving player data! " + e.getMessage()); + } + + // Closing DB connection db.close(); } @@ -131,6 +161,7 @@ public class SimpleAuth implements DedicatedServerModInitializer { // Marking player as not authenticated, (re)setting login tries to zero String uuid = convertUuid(player); SimpleAuth.deauthenticatedUsers.put(uuid, new PlayerCache(uuid, player)); + // Teleporting player to spawn to hide its position if(config.main.spawnOnJoin) teleportPlayer(player, true); 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 56d7f8d..2899768 100644 --- a/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java +++ b/src/main/java/org/samo_lego/simpleauth/commands/AuthCommand.java @@ -2,6 +2,7 @@ package org.samo_lego.simpleauth.commands; import com.google.gson.JsonObject; import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.command.arguments.BlockPosArgumentType; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.ServerCommandSource; @@ -50,28 +51,23 @@ public class AuthCommand { ) ) .then(literal("setSpawn") - .then(argument("here", word()) - .executes( ctx -> setSpawn( + .executes( ctx -> setSpawn( + ctx.getSource(), + Objects.requireNonNull(ctx.getSource().getEntity()).dimension.getRawId(), + ctx.getSource().getEntity().getX(), + ctx.getSource().getEntity().getY(), + ctx.getSource().getEntity().getZ() + )) + .then(argument("dimension id", integer()) + .then(argument("position", BlockPosArgumentType.blockPos()) + .executes(ctx -> setSpawn( ctx.getSource(), - Objects.requireNonNull(ctx.getSource().getEntity()).dimension.getRawId(), - ctx.getSource().getEntity().getX(), - ctx.getSource().getEntity().getY(), - ctx.getSource().getEntity().getZ() - )) - ) - .then(argument("dimensionId", integer()) - .then(argument("x", integer()) - .then(argument("y", integer()) - .then(argument("z", integer()) - .executes( ctx -> setSpawn( - ctx.getSource(), - getInteger(ctx, "dimensionId"), - getInteger(ctx, "x"), - getInteger(ctx, "y"), - getInteger(ctx, "z") - )) - ) - ) + getInteger(ctx, "dimension id"), + BlockPosArgumentType.getLoadedBlockPos(ctx, "position").getX(), + // +1 to not spawn player in ground + BlockPosArgumentType.getLoadedBlockPos(ctx, "position").getY() + 1, + BlockPosArgumentType.getLoadedBlockPos(ctx, "position").getZ() + )) ) ) ) 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 b2d346f..6dbfc8e 100644 --- a/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java +++ b/src/main/java/org/samo_lego/simpleauth/event/AuthEventHandler.java @@ -75,22 +75,29 @@ 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 (isPlayerFake(player)) return; // Checking if session is still valid String uuid = convertUuid(player); PlayerCache playerCache = deauthenticatedUsers.getOrDefault(uuid, null); - if ( - playerCache != null && - playerCache.wasAuthenticated && - playerCache.validUntil >= System.currentTimeMillis() && - playerCache.lastIp.equals(player.getIp()) - ) { - deauthenticatedUsers.remove(uuid); // Makes player authenticated - return; + if (playerCache != null) { + if ( + playerCache.wasAuthenticated && + playerCache.validUntil >= System.currentTimeMillis() && + playerCache.lastIp.equals(player.getIp()) + ) { + deauthenticatedUsers.remove(uuid); // Makes player authenticated + return; + } + // Invalidating session + playerCache.wasAuthenticated = false; } - deauthenticatePlayer(player); + else + deauthenticatePlayer(player); + + 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)) { @@ -174,6 +181,7 @@ public class AuthEventHandler { PlayerCache playerCache = deauthenticatedUsers.get(convertUuid(player)); if(playerCache == null) return; + playerCache.wasAuthenticated = true; // Setting the session expire time playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000; diff --git a/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerPlayNetworkHandler.java b/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerPlayNetworkHandler.java index 622deb3..06bd265 100644 --- a/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerPlayNetworkHandler.java +++ b/src/main/java/org/samo_lego/simpleauth/mixin/MixinServerPlayNetworkHandler.java @@ -6,13 +6,10 @@ import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; import net.minecraft.util.ActionResult; -import net.minecraft.world.dimension.DimensionType; import org.samo_lego.simpleauth.event.entity.player.ChatCallback; import org.samo_lego.simpleauth.event.entity.player.PlayerMoveCallback; import org.samo_lego.simpleauth.event.item.TakeItemCallback; -import org.samo_lego.simpleauth.storage.PlayerCache; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -21,9 +18,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import static net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action.SWAP_HELD_ITEMS; -import static org.samo_lego.simpleauth.SimpleAuth.config; -import static org.samo_lego.simpleauth.SimpleAuth.deauthenticatedUsers; -import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; @Mixin(ServerPlayNetworkHandler.class) public abstract class MixinServerPlayNetworkHandler { @@ -86,48 +80,4 @@ public abstract class MixinServerPlayNetworkHandler { ci.cancel(); } } - - @Inject( - method="disconnect(Lnet/minecraft/text/Text;)V", - at = @At(value = "HEAD"), - cancellable = true - ) - // If player is disconnected because of sth (e.g. wrong password) - // its position is set back to previous (e.g. not spawn) - private void disconnect(Text reason, CallbackInfo ci) { - if(config.main.spawnOnJoin) { - PlayerCache cache = deauthenticatedUsers.get(convertUuid(player)); - // Puts player to last cached position - this.player.teleport( - this.server.getWorld(DimensionType.byRawId(cache.lastDimId)), - cache.lastX, - cache.lastY, - cache.lastZ, - 0, - 0 - ); - } - } - - @Inject( - method="onDisconnected(Lnet/minecraft/text/Text;)V", - at = @At(value = "HEAD"), - cancellable = true - ) - // If player is disconnected because of sth (e.g. wrong password) - // its position is set back to previous (e.g. not spawn) - private void onDisconnected(Text reason, CallbackInfo ci) { - if(config.main.spawnOnJoin) { - PlayerCache cache = deauthenticatedUsers.get(convertUuid(player)); - // Puts player to last cached position - this.player.teleport( - this.server.getWorld(DimensionType.byRawId(cache.lastDimId)), - cache.lastX, - cache.lastY, - cache.lastZ, - 0, - 0 - ); - } - } } diff --git a/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java b/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java index d5e252f..dc07531 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java @@ -61,7 +61,7 @@ public class AuthConfig { public double x; public double y; public double z; - }; + } } public static class LangConfig { public String enterPassword = "§6You need to enter your password!"; @@ -91,7 +91,7 @@ public class AuthConfig { public String minPasswordChars = "§6Password needs to be at least %d characters long!"; public String disallowedUsername = "§6Invalid username characters! Allowed character regex: %s"; public String playerAlreadyOnline = "§cPlayer %s is already online!"; - public String worldSpawnSet = ""; + public String worldSpawnSet = "§aSpawn for logging in was set successfully."; } public static class ExperimentalConfig { // Prevents player being kicked because another player with the same name has joined the server 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 4b2a1f1..b15bf5f 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java @@ -1,7 +1,9 @@ package org.samo_lego.simpleauth.storage; +import com.google.gson.Gson; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import net.minecraft.server.network.ServerPlayerEntity; import static org.samo_lego.simpleauth.SimpleAuth.config; @@ -20,12 +22,13 @@ public class PlayerCache { public double lastY; public double lastZ; - private static final JsonParser parser = new JsonParser(); + private static final Gson gson = new Gson(); public PlayerCache(String uuid, ServerPlayerEntity player) { if(db.isClosed()) return; + if(player != null) { this.lastIp = player.getIp(); @@ -45,18 +48,47 @@ public class PlayerCache { this.lastZ = config.worldSpawn.z; } - this.wasAuthenticated = false; - this.loginTries = 0; - - if(db.isUserRegistered(uuid)) { - this.isRegistered = true; - JsonObject json = parser.parse(db.getData(uuid)).getAsJsonObject(); + String data = db.getData(uuid); + + // Getting (hashed) password + JsonObject json = gson.fromJson(data, JsonObject.class); this.password = json.get("password").getAsString(); + + // If coordinates are same as the one from world spawn + // we should check the DB for saved coords + if(config.main.spawnOnJoin) { + try { + JsonElement lastLoc = json.get("lastLocation"); + if ( + lastLoc != null && + this.lastDimId == config.worldSpawn.dimensionId && + this.lastX == config.worldSpawn.x && + this.lastY == config.worldSpawn.y && + this.lastZ == config.worldSpawn.z + ) { + // Getting DB coords + JsonObject lastLocation = gson.fromJson(lastLoc.getAsString(), JsonObject.class); + this.lastDimId = lastLocation.get("dimId").getAsInt(); + this.lastX = lastLocation.get("x").getAsDouble(); + this.lastY = lastLocation.get("y").getAsDouble(); + this.lastZ = lastLocation.get("z").getAsDouble(); + + // Removing location data from DB + json.remove("lastLocation"); + db.updateUserData(uuid, json.toString()); + } + } catch (JsonSyntaxException ignored) { + // Player didn't have any coords in db to tp to + } + } + this.isRegistered = true; } else { this.isRegistered = false; this.password = ""; } + this.wasAuthenticated = false; + this.loginTries = 0; } } 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 622b32e..94b746a 100644 --- a/src/main/java/org/samo_lego/simpleauth/storage/SimpleAuthDatabase.java +++ b/src/main/java/org/samo_lego/simpleauth/storage/SimpleAuthDatabase.java @@ -15,7 +15,7 @@ import static org.iq80.leveldb.impl.Iq80DBFactory.factory; public class SimpleAuthDatabase { private static final Logger LOGGER = LogManager.getLogger(); - private DB levelDBStore; + public DB levelDBStore; // Connects to the DB public void openConnection() { @@ -81,7 +81,7 @@ public class SimpleAuthDatabase { // Updates the password of the user public void updateUserData(String uuid, String data) { try { - levelDBStore.put(bytes("UUID:" + uuid),bytes("data:" + data)); + levelDBStore.put(bytes("UUID:" + uuid), bytes("data:" + data)); } catch (Error e) { LOGGER.error("[SimpleAuth] " + e.getMessage()); } @@ -90,7 +90,7 @@ public class SimpleAuthDatabase { // Gets the hashed password from DB public String getData(String uuid){ try { - if(this.isUserRegistered(uuid)) // Gets password from db and removes "password:" prefix from it + if(this.isUserRegistered(uuid)) // Gets password from db and removes "data:" prefix from it return new String(levelDBStore.get(bytes("UUID:" + uuid))).substring(5); } catch (Error e) { LOGGER.error("[SimpleAuth] Error getting password: " + e.getMessage());