Merge branch 'fabric' into mongodb

# Conflicts:
#	src/main/java/org/samo_lego/simpleauth/SimpleAuth.java
#	src/main/java/org/samo_lego/simpleauth/storage/AuthConfig.java
#	src/main/java/org/samo_lego/simpleauth/storage/PlayerCache.java
This commit is contained in:
samo_lego 2020-10-16 22:45:51 +02:00
commit ae8cdd38fd
14 changed files with 348 additions and 299 deletions

View File

@ -63,5 +63,5 @@ jobs:
# The path to the asset you want to upload # The path to the asset you want to upload
asset_path: build/libs/${{ env.BUILDNAME }}.jar asset_path: build/libs/${{ env.BUILDNAME }}.jar
# The name of the asset you want to upload # The name of the asset you want to upload
asset_name: ${{ env.BUILDNAME }} asset_name: ${{ env.PROJECT_ID }}_fabric-${{ env.VERSION }}-${{ env.MC_VERSION }}.jar
asset_content_type: application/zip asset_content_type: application/zip

View File

@ -10,7 +10,7 @@ loader_version=0.9.3+build.207
fabric_version=0.20.2+build.402-1.16 fabric_version=0.20.2+build.402-1.16
# Mod Properties # Mod Properties
mod_version = 1.5.3 mod_version = 1.6.0
maven_group = org.samo_lego maven_group = org.samo_lego
archives_base_name = simpleauth archives_base_name = simpleauth
@ -21,4 +21,4 @@ bytes_version = 1.3.0
# Carpet for debugging # Carpet for debugging
carpet_core_version = 1.4.0+v200623 carpet_core_version = 1.4.0+v200623
carpet_branch = master carpet_branch = master

View File

@ -5,7 +5,6 @@ import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.player.*; import net.fabricmc.fabric.api.event.player.*;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
@ -30,17 +29,16 @@ import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Properties; import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.iq80.leveldb.impl.Iq80DBFactory.bytes; import static org.iq80.leveldb.impl.Iq80DBFactory.bytes;
import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake; import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake;
import static org.samo_lego.simpleauth.commands.AuthCommand.reloadConfig;
import static org.samo_lego.simpleauth.event.AuthEventHandler.*;
import static org.samo_lego.simpleauth.utils.SimpleLogger.logError; 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.SimpleLogger.logInfo;
import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid;
public class SimpleAuth implements DedicatedServerModInitializer { public class SimpleAuth implements DedicatedServerModInitializer {
@ -48,27 +46,13 @@ public class SimpleAuth implements DedicatedServerModInitializer {
public static final ExecutorService THREADPOOL = Executors.newCachedThreadPool(); public static final ExecutorService THREADPOOL = Executors.newCachedThreadPool();
private static final Timer TIMER = new Timer();
/** /**
* HashMap of players that have joined the server. * HashMap of players that have joined the server.
* It's cleared on server stop in order to save some interactions with database during runtime. * 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<String, PlayerCache> playerCacheMap = new HashMap<>(); public static HashMap<String, PlayerCache> 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 // Getting game directory
public static final Path gameDirectory = FabricLoader.getInstance().getGameDir(); public static final Path gameDirectory = FabricLoader.getInstance().getGameDir();
@ -139,8 +123,6 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// Closing threads // Closing threads
try { try {
THREADPOOL.shutdownNow(); THREADPOOL.shutdownNow();
TIMER.cancel();
TIMER.purge();
if (!THREADPOOL.awaitTermination(100, TimeUnit.MICROSECONDS)) { if (!THREADPOOL.awaitTermination(100, TimeUnit.MICROSECONDS)) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
@ -152,156 +134,4 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// Closing DB connection // Closing DB connection
DB.close(); 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);
playerCacheMap.put(uuid, PlayerCache.fromJson(player, DB.getUserData(uuid)));
playerCacheMap.get(uuid).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,
90,
90
);
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
));
}
}
} }

View File

@ -5,15 +5,14 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import org.samo_lego.simpleauth.SimpleAuth;
import org.samo_lego.simpleauth.utils.AuthHelper; 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.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.word; import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal; import static net.minecraft.server.command.CommandManager.literal;
import static org.samo_lego.simpleauth.SimpleAuth.*; import static org.samo_lego.simpleauth.SimpleAuth.*;
import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid;
public class AccountCommand { public class AccountCommand {
@ -36,7 +35,7 @@ public class AccountCommand {
) )
) )
) )
.then(literal("changePassword") .then(literal("changePassword") //todo mongodb update
.then(argument("old password", word()) .then(argument("old password", word())
.executes(ctx -> { .executes(ctx -> {
ctx.getSource().getPlayer().sendMessage( ctx.getSource().getPlayer().sendMessage(
@ -72,13 +71,10 @@ public class AccountCommand {
// Different thread to avoid lag spikes // Different thread to avoid lag spikes
THREADPOOL.submit(() -> { THREADPOOL.submit(() -> {
if (AuthHelper.checkPassword(convertUuid(player), pass.toCharArray()) == 1) { if (AuthHelper.checkPassword(((PlayerAuth) player).getFakeUuid(), pass.toCharArray()) == 1) {
DB.deleteUserData(convertUuid(player)); DB.deleteUserData(((PlayerAuth) player).getFakeUuid());
player.sendMessage( player.sendMessage(new LiteralText(config.lang.accountDeleted), false);
new LiteralText(config.lang.accountDeleted), ((PlayerAuth) player).setAuthenticated(false);
false
);
SimpleAuth.deauthenticatePlayer(player);
return; return;
} }
player.sendMessage( player.sendMessage(
@ -103,7 +99,7 @@ public class AccountCommand {
} }
// Different thread to avoid lag spikes // Different thread to avoid lag spikes
THREADPOOL.submit(() -> { 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) { if (newPass.length() < config.main.minPasswordChars) {
player.sendMessage(new LiteralText( player.sendMessage(new LiteralText(
String.format(config.lang.minPasswordChars, config.main.minPasswordChars) String.format(config.lang.minPasswordChars, config.main.minPasswordChars)
@ -117,7 +113,7 @@ public class AccountCommand {
return; return;
} }
// Changing password in playercache // 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( player.sendMessage(
new LiteralText(config.lang.passwordUpdated), new LiteralText(config.lang.passwordUpdated),
false false

View File

@ -6,13 +6,13 @@ import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import org.samo_lego.simpleauth.utils.AuthHelper; 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.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.word; import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal; import static net.minecraft.server.command.CommandManager.literal;
import static org.samo_lego.simpleauth.SimpleAuth.*; import static org.samo_lego.simpleauth.SimpleAuth.*;
import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid;
public class LoginCommand { public class LoginCommand {
@ -32,8 +32,8 @@ public class LoginCommand {
private static int login(ServerCommandSource source, String pass) throws CommandSyntaxException { private static int login(ServerCommandSource source, String pass) throws CommandSyntaxException {
// Getting the player who send the command // Getting the player who send the command
ServerPlayerEntity player = source.getPlayer(); ServerPlayerEntity player = source.getPlayer();
String uuid = convertUuid(player); String uuid = ((PlayerAuth) player).getFakeUuid();
if (isAuthenticated(player)) { if (((PlayerAuth) player).isAuthenticated()) {
player.sendMessage(new LiteralText(config.lang.alreadyAuthenticated), false); player.sendMessage(new LiteralText(config.lang.alreadyAuthenticated), false);
return 0; return 0;
} }
@ -47,7 +47,8 @@ public class LoginCommand {
return; return;
} }
else if(passwordResult == 1) { else if(passwordResult == 1) {
authenticatePlayer(player, new LiteralText(config.lang.successfullyAuthenticated)); player.sendMessage(new LiteralText(config.lang.successfullyAuthenticated), false);
((PlayerAuth) player).setAuthenticated(true);
return; return;
} }
else if(passwordResult == -1) { else if(passwordResult == -1) {

View File

@ -5,11 +5,10 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; 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 net.minecraft.server.command.CommandManager.literal;
import static org.samo_lego.simpleauth.SimpleAuth.*; import static org.samo_lego.simpleauth.SimpleAuth.config;
import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid;
public class LogoutCommand { public class LogoutCommand {
@ -22,15 +21,8 @@ public class LogoutCommand {
private static int logout(ServerCommandSource serverCommandSource) throws CommandSyntaxException { private static int logout(ServerCommandSource serverCommandSource) throws CommandSyntaxException {
ServerPlayerEntity player = serverCommandSource.getPlayer(); ServerPlayerEntity player = serverCommandSource.getPlayer();
PlayerCache playerCache = playerCacheMap.get(convertUuid(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;
deauthenticatePlayer(player); ((PlayerAuth) player).setAuthenticated(false);
player.sendMessage(new LiteralText(config.lang.successfulLogout), false); player.sendMessage(new LiteralText(config.lang.successfulLogout), false);
return 1; return 1;
} }

View File

@ -6,6 +6,7 @@ import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import org.samo_lego.simpleauth.storage.PlayerCache; 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.getString;
import static com.mojang.brigadier.arguments.StringArgumentType.word; 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 net.minecraft.server.command.CommandManager.literal;
import static org.samo_lego.simpleauth.SimpleAuth.*; import static org.samo_lego.simpleauth.SimpleAuth.*;
import static org.samo_lego.simpleauth.utils.AuthHelper.hashPassword; import static org.samo_lego.simpleauth.utils.AuthHelper.hashPassword;
import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid;
public class RegisterCommand { public class RegisterCommand {
@ -39,7 +39,7 @@ public class RegisterCommand {
player.sendMessage(new LiteralText(config.lang.loginRequired), false); player.sendMessage(new LiteralText(config.lang.loginRequired), false);
return 0; return 0;
} }
else if(isAuthenticated(player)) { else if(((PlayerAuth) player).isAuthenticated()) {
player.sendMessage(new LiteralText(config.lang.alreadyAuthenticated), false); player.sendMessage(new LiteralText(config.lang.alreadyAuthenticated), false);
return 0; return 0;
} }
@ -62,9 +62,11 @@ public class RegisterCommand {
return; return;
} }
PlayerCache playerCache = playerCacheMap.get(convertUuid(player)); PlayerCache playerCache = playerCacheMap.get(((PlayerAuth) player).getFakeUuid());
if (!playerCache.isRegistered) { 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.password = hashPassword(pass1.toCharArray());
playerCache.isRegistered = true; playerCache.isRegistered = true;
return; return;

View File

@ -13,13 +13,14 @@ import net.minecraft.util.ActionResult;
import net.minecraft.util.TypedActionResult; import net.minecraft.util.TypedActionResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import org.samo_lego.simpleauth.storage.PlayerCache; import org.samo_lego.simpleauth.storage.PlayerCache;
import org.samo_lego.simpleauth.utils.PlayerAuth;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static org.samo_lego.simpleauth.SimpleAuth.*; import static org.samo_lego.simpleauth.SimpleAuth.config;
import static org.samo_lego.simpleauth.utils.UuidConverter.convertUuid; import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap;
/** /**
* This class will take care of actions players try to do, * This class will take care of actions players try to do,
@ -40,7 +41,7 @@ public class AuthEventHandler {
Pattern pattern = Pattern.compile(regex); Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(incomingPlayerUsername); 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 // Player needs to be kicked, since there's already a player with that name
// playing on the server // playing on the server
return new LiteralText( return new LiteralText(
@ -63,10 +64,10 @@ public class AuthEventHandler {
// Player joining the server // Player joining the server
public static void onPlayerJoin(ServerPlayerEntity player) { public static void onPlayerJoin(ServerPlayerEntity player) {
// If player is fake auth is not needed // If player is fake auth is not needed
if (isPlayerFake(player)) if (((PlayerAuth) player).canSkipAuth())
return; return;
// Checking if session is still valid // Checking if session is still valid
String uuid = convertUuid(player); String uuid = ((PlayerAuth) player).getFakeUuid();
PlayerCache playerCache = playerCacheMap.getOrDefault(uuid, null); PlayerCache playerCache = playerCacheMap.getOrDefault(uuid, null);
if (playerCache != null) { if (playerCache != null) {
@ -75,7 +76,7 @@ public class AuthEventHandler {
playerCache.validUntil >= System.currentTimeMillis() && playerCache.validUntil >= System.currentTimeMillis() &&
player.getIp().equals(playerCache.lastIp) player.getIp().equals(playerCache.lastIp)
) { ) {
authenticatePlayer(player, null); // Makes player authenticated ((PlayerAuth) player).setAuthenticated(true);
return; return;
} }
player.setInvulnerable(config.experimental.playerInvulnerable); player.setInvulnerable(config.experimental.playerInvulnerable);
@ -83,17 +84,15 @@ public class AuthEventHandler {
// Invalidating session // Invalidating session
playerCache.isAuthenticated = false; playerCache.isAuthenticated = false;
player.sendMessage(notAuthenticated(player), false); if(config.main.spawnOnJoin)
((PlayerAuth) player).hidePosition(true);
} }
else { else {
deauthenticatePlayer(player); ((PlayerAuth) player).setAuthenticated(false);
playerCache = playerCacheMap.get(uuid); playerCache = playerCacheMap.get(uuid);
playerCache.wasOnFire = false; playerCache.wasOnFire = false;
} }
if(config.main.spawnOnJoin)
teleportPlayer(player, true);
// Tries to rescue player from nether portal // Tries to rescue player from nether portal
if(config.main.tryPortalRescue && player.getBlockState().getBlock().equals(Blocks.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) { public static void onPlayerLeave(ServerPlayerEntity player) {
if(isPlayerFake(player)) if(((PlayerAuth) player).canSkipAuth())
return; return;
String uuid = convertUuid(player); String uuid = ((PlayerAuth) player).getFakeUuid();
PlayerCache playerCache = playerCacheMap.get(uuid); PlayerCache playerCache = playerCacheMap.get(uuid);
playerCache.lastIp = player.getIp(); if(((PlayerAuth) player).isAuthenticated()) {
playerCache.lastAir = player.getAir(); playerCache.lastIp = player.getIp();
playerCache.wasOnFire = player.isOnFire(); playerCache.lastAir = player.getAir();
playerCache.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL); playerCache.wasOnFire = player.isOnFire();
if(isAuthenticated(player)) { playerCache.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL);
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;
// Setting the session expire time // Setting the session expire time
if(config.main.sessionTimeoutTime != -1) if(config.main.sessionTimeoutTime != -1)
playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000; playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000;
} }
else {
((PlayerAuth) player).hidePosition(false);
}
} }
// Player chatting // Player chatting
@ -140,12 +136,12 @@ public class AuthEventHandler {
// Getting the message to then be able to check it // Getting the message to then be able to check it
String msg = chatMessageC2SPacket.getChatMessage(); String msg = chatMessageC2SPacket.getChatMessage();
if( if(
!isAuthenticated((ServerPlayerEntity) player) && !((PlayerAuth) player).isAuthenticated() &&
!msg.startsWith("/login") && !msg.startsWith("/login") &&
!msg.startsWith("/register") && !msg.startsWith("/register") &&
(!config.experimental.allowChat || msg.startsWith("/")) (!config.experimental.allowChat || msg.startsWith("/"))
) { ) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return ActionResult.FAIL; return ActionResult.FAIL;
} }
return ActionResult.PASS; return ActionResult.PASS;
@ -154,7 +150,7 @@ public class AuthEventHandler {
// Player movement // Player movement
public static ActionResult onPlayerMove(PlayerEntity player) { public static ActionResult onPlayerMove(PlayerEntity player) {
// Player will fall if enabled (prevent fly kick) // 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(!auth && config.main.allowFalling && !player.isOnGround() && !player.isInsideWaterOrBubbleColumn()) {
if(player.isInvulnerable()) if(player.isInvulnerable())
player.setInvulnerable(false); player.setInvulnerable(false);
@ -171,8 +167,8 @@ public class AuthEventHandler {
// Using a block (right-click function) // Using a block (right-click function)
public static ActionResult onUseBlock(PlayerEntity player) { public static ActionResult onUseBlock(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowBlockUse) { if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowBlockUse) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return ActionResult.FAIL; return ActionResult.FAIL;
} }
return ActionResult.PASS; return ActionResult.PASS;
@ -180,8 +176,8 @@ public class AuthEventHandler {
// Breaking a block // Breaking a block
public static boolean onBreakBlock(PlayerEntity player) { public static boolean onBreakBlock(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowBlockPunch) { if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowBlockPunch) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return false; return false;
} }
return true; return true;
@ -189,8 +185,8 @@ public class AuthEventHandler {
// Using an item // Using an item
public static TypedActionResult<ItemStack> onUseItem(PlayerEntity player) { public static TypedActionResult<ItemStack> onUseItem(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowItemUse) { if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowItemUse) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return TypedActionResult.fail(ItemStack.EMPTY); return TypedActionResult.fail(ItemStack.EMPTY);
} }
@ -198,16 +194,16 @@ public class AuthEventHandler {
} }
// Dropping an item // Dropping an item
public static ActionResult onDropItem(PlayerEntity player) { public static ActionResult onDropItem(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowItemDrop) { if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowItemDrop) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return ActionResult.FAIL; return ActionResult.FAIL;
} }
return ActionResult.PASS; return ActionResult.PASS;
} }
// Changing inventory (item moving etc.) // Changing inventory (item moving etc.)
public static ActionResult onTakeItem(PlayerEntity player) { public static ActionResult onTakeItem(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowItemMoving) { if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowItemMoving) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return ActionResult.FAIL; return ActionResult.FAIL;
} }
@ -215,8 +211,8 @@ public class AuthEventHandler {
} }
// Attacking an entity // Attacking an entity
public static ActionResult onAttackEntity(PlayerEntity player) { public static ActionResult onAttackEntity(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.experimental.allowEntityPunch) { if(!((PlayerAuth) player).isAuthenticated() && !config.experimental.allowEntityPunch) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return ActionResult.FAIL; return ActionResult.FAIL;
} }
@ -224,8 +220,8 @@ public class AuthEventHandler {
} }
// Interacting with entity // Interacting with entity
public static ActionResult onUseEntity(PlayerEntity player) { public static ActionResult onUseEntity(PlayerEntity player) {
if(!isAuthenticated((ServerPlayerEntity) player) && !config.main.allowEntityInteract) { if(!((PlayerAuth) player).isAuthenticated() && !config.main.allowEntityInteract) {
player.sendMessage(notAuthenticated(player), false); player.sendMessage(((PlayerAuth) player).getAuthMessage(), false);
return ActionResult.FAIL; return ActionResult.FAIL;
} }

View File

@ -1,17 +1,216 @@
package org.samo_lego.simpleauth.mixin; package org.samo_lego.simpleauth.mixin;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; 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.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.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.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 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) @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"), cancellable = true)
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--;
}
ci.cancel();
}
}
// Player item dropping // Player item dropping
@Inject(method = "dropSelectedItem(Z)Z", at = @At("HEAD"), cancellable = true) @Inject(method = "dropSelectedItem(Z)Z", at = @At("HEAD"), cancellable = true)
private void dropSelectedItem(boolean dropEntireStack, CallbackInfoReturnable<Boolean> cir) { private void dropSelectedItem(boolean dropEntireStack, CallbackInfoReturnable<Boolean> cir) {

View File

@ -25,7 +25,7 @@ public abstract class MixinPlayerManager {
PlayerJoinServerCallback.EVENT.invoker().onPlayerJoin(serverPlayerEntity); 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) { private void onPlayerLeave(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) {
PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity); PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity);
} }

View File

@ -86,12 +86,12 @@ public class AuthConfig {
public boolean allowFalling = false; public boolean allowFalling = false;
/** /**
* Whether to tp player to spawn when joining (to hide coordinates). * Whether to tp player to spawn when joining (to hide original player coordinates).
*/ */
public boolean spawnOnJoin = false; public boolean spawnOnJoin = false;
/** /**
* Data for spawn (where deauthenticated players are teleported). * Data for spawn (where deauthenticated players are teleported temporarily).
* @see <a href="https://github.com/samolego/SimpleAuth/wiki/Coordinate-Hiding" target="_blank">wiki</a> * @see <a href="https://github.com/samolego/SimpleAuth/wiki/Coordinate-Hiding" target="_blank">wiki</a>
*/ */
public static class WorldSpawn { public static class WorldSpawn {

View File

@ -4,6 +4,14 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.server.network.ServerPlayerEntity; 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 java.util.Objects; import java.util.Objects;
@ -52,12 +60,10 @@ public class PlayerCache {
* Last recorded position before de-authentication. * Last recorded position before de-authentication.
*/ */
public static class LastLocation { public static class LastLocation {
public String lastDim; public ServerWorld dimension;
public double lastX; public Vec3d position;
public double lastY; public float yaw;
public double lastZ; public float pitch;
public float lastYaw;
public float lastPitch;
} }
public PlayerCache.LastLocation lastLocation = new PlayerCache.LastLocation(); public PlayerCache.LastLocation lastLocation = new PlayerCache.LastLocation();
@ -79,13 +85,12 @@ public class PlayerCache {
this.lastAir = player.getAir(); this.lastAir = player.getAir();
// Setting position cache // 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.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 { else {
this.wasOnFire = false; this.wasOnFire = false;

View File

@ -0,0 +1,61 @@
package org.samo_lego.simpleauth.utils;
import net.minecraft.text.Text;
/**
* PLayer authentication extension.
*/
public interface PlayerAuth {
/**
* 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
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
*/
void hidePosition(boolean hide);
/**
* 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
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
*/
String getFakeUuid();
/**
* Sets the authentication status of the player.
*
* @param authenticated whether player should be authenticated
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
*/
void setAuthenticated(boolean authenticated);
/**
* Checks whether player is authenticated.
*
* @return false if player is not authenticated, otherwise true.
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
*/
boolean isAuthenticated();
/**
* Gets the text which tells the player
* to login or register, depending on account status.
*
* @return LiteralText with appropriate string (login or register)
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
*/
Text getAuthMessage();
/**
* Checks whether player is a fake player (from CarpetMod).
*
* @return true if player is fake (can skip authentication process), otherwise false
* @see <a href="https://samolego.github.io/SimpleAuth/org/samo_lego/simpleauth/mixin/MixinPlayerEntity.html">See implementation</a>
*/
boolean canSkipAuth();
}

View File

@ -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();
}
}