Code cleanup

This commit is contained in:
samo_lego 2020-10-29 13:02:50 +01:00
parent 4247fad3c0
commit b297ae6cbe
7 changed files with 235 additions and 292 deletions

View File

@ -9,6 +9,7 @@ import org.samo_lego.simpleauth.utils.PlayerAuth;
import static net.minecraft.server.command.CommandManager.literal;
import static org.samo_lego.simpleauth.SimpleAuth.config;
import static org.samo_lego.simpleauth.SimpleAuth.mojangAccountNamesCache;
public class LogoutCommand {
@ -22,8 +23,12 @@ public class LogoutCommand {
private static int logout(ServerCommandSource serverCommandSource) throws CommandSyntaxException {
ServerPlayerEntity player = serverCommandSource.getPlayer();
((PlayerAuth) player).setAuthenticated(false);
player.sendMessage(new LiteralText(config.lang.successfulLogout), false);
if(mojangAccountNamesCache.contains(player.getGameProfile().getName().toLowerCase())) {
((PlayerAuth) player).setAuthenticated(false);
player.sendMessage(new LiteralText(config.lang.successfulLogout), false);
}
else
player.sendMessage(new LiteralText(config.lang.cannotLogout), false);
return 1;
}
}

View File

@ -63,7 +63,6 @@ public class AuthEventHandler {
// Player joining the server
public static void onPlayerJoin(ServerPlayerEntity player) {
// If player is fake auth is not needed
if (((PlayerAuth) player).canSkipAuth())
return;
// Checking if session is still valid
@ -76,22 +75,12 @@ public class AuthEventHandler {
playerCache.validUntil >= System.currentTimeMillis() &&
player.getIp().equals(playerCache.lastIp)
) {
// Valid session
((PlayerAuth) player).setAuthenticated(true);
return;
}
player.setInvulnerable(config.experimental.playerInvulnerable);
player.setInvisible(config.experimental.playerInvisible);
// Invalidating session
playerCache.isAuthenticated = false;
if(config.main.spawnOnJoin)
((PlayerAuth) player).hidePosition(true);
}
else {
((PlayerAuth) player).setAuthenticated(false);
playerCache = playerCacheMap.get(uuid);
playerCache.wasOnFire = false;
}
((PlayerAuth) player).setAuthenticated(false);
// Tries to rescue player from nether portal
@ -116,10 +105,8 @@ public class AuthEventHandler {
String uuid = ((PlayerAuth) player).getFakeUuid();
PlayerCache playerCache = playerCacheMap.get(uuid);
if(((PlayerAuth) player).isAuthenticated()) {
if(playerCache.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
@ -151,13 +138,8 @@ public class AuthEventHandler {
public static ActionResult onPlayerMove(PlayerEntity player) {
// Player will fall if enabled (prevent fly kick)
boolean auth = ((PlayerAuth) player).isAuthenticated();
if(!auth && config.main.allowFalling && !player.isOnGround() && !player.isInsideWaterOrBubbleColumn()) {
if(player.isInvulnerable())
player.setInvulnerable(false);
return ActionResult.PASS;
}
// Otherwise movement should be disabled
else if(!auth && !config.experimental.allowMovement) {
if(!auth && !config.experimental.allowMovement) {
if(!player.isInvulnerable())
player.setInvulnerable(true);
return ActionResult.FAIL;

View File

@ -1,231 +1,17 @@
package org.samo_lego.simpleauth.mixin;
import com.mojang.authlib.GameProfile;
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.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import static org.samo_lego.simpleauth.SimpleAuth.config;
import static org.samo_lego.simpleauth.SimpleAuth.playerCacheMap;
import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake;
import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo;
@Mixin(PlayerEntity.class)
public abstract class MixinPlayerEntity implements PlayerAuth {
public class MixinPlayerEntity {
@Shadow @Final private GameProfile gameProfile;
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());
if(config.main.spawnOnJoin)
logInfo("Teleporting " + player.getName().asString() + (hide ? " to spawn." : " to original position."));
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() && this.isUsingMojangAccount())
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.
*/
// Getting player+s name via GameProfile, in order to be compatible with Drogtor mod
String playername = player.getGameProfile().getName().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 can skip authentication process.
*
* @return true if 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)) || (isUsingMojangAccount() && config.experimental.premiumAutologin);
}
/**
* Whether the player is using the mojang account.
* @return true if paid, otherwise false
*/
@Override
public boolean isUsingMojangAccount() {
return !gameProfile.getId().equals(PlayerEntity.getOfflinePlayerUuid(gameProfile.getName()));
}
/**
* Checks whether player is authenticated.
*
* @return false if player is not authenticated, otherwise true.
*/
@Override
public boolean isAuthenticated() {
String uuid = ((PlayerAuth) player).getFakeUuid();
return this.canSkipAuth() || (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
@Inject(method = "dropSelectedItem(Z)Z", at = @At("HEAD"), cancellable = true)
@ -234,14 +20,6 @@ public abstract class MixinPlayerEntity implements PlayerAuth {
ActionResult result = DropItemCallback.EVENT.invoker().onDropItem(player);
if (result == ActionResult.FAIL) {
// Canceling the item drop, as well as giving the items back to player (and updating inv with packet)
player.networkHandler.sendPacket(
new ScreenHandlerSlotUpdateS2CPacket(
-2,
player.inventory.selectedSlot,
player.inventory.getStack(player.inventory.selectedSlot))
);
player.networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(-1, -1, player.inventory.getCursorStack()));
cir.setReturnValue(false);
}
}

View File

@ -0,0 +1,216 @@
package org.samo_lego.simpleauth.mixin;
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.samo_lego.simpleauth.SimpleAuth;
import org.samo_lego.simpleauth.storage.PlayerCache;
import org.samo_lego.simpleauth.utils.PlayerAuth;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static org.samo_lego.simpleauth.SimpleAuth.*;
import static org.samo_lego.simpleauth.utils.CarpetHelper.isPlayerCarpetFake;
import static org.samo_lego.simpleauth.utils.SimpleLogger.logInfo;
@Mixin(ServerPlayerEntity.class)
public class MixinServerPlayerEntity 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");
@Final
@Shadow
public MinecraftServer server;
/**
* 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());
if(config.main.spawnOnJoin)
logInfo("Teleporting " + player.getName().asString() + (hide ? " to spawn." : " to original position."));
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() && this.isUsingMojangAccount())
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.
*/
// Getting player+s name via GameProfile, in order to be compatible with Drogtor mod
String playername = player.getGameProfile().getName().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);
}
}
}
/**
* 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 can skip authentication process.
*
* @return true if 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)) || (isUsingMojangAccount() && config.experimental.premiumAutologin);
}
/**
* Whether the player is using the mojang account.
* @return true if paid, otherwise false
*/
@Override
public boolean isUsingMojangAccount() {
return mojangAccountNamesCache.contains(player.getGameProfile().getName().toLowerCase());
}
/**
* Checks whether player is authenticated.
*
* @return false if player is not authenticated, otherwise true.
*/
@Override
public boolean isAuthenticated() {
String uuid = ((PlayerAuth) player).getFakeUuid();
return this.canSkipAuth() || (playerCacheMap.containsKey(uuid) && playerCacheMap.get(uuid).isAuthenticated);
}
@Inject(method = "playerTick()V", at = @At("HEAD"), cancellable = true)
private void playerTick(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();
}
}
}

View File

@ -81,11 +81,6 @@ public class AuthConfig {
*/
public int sessionTimeoutTime = 60;
/**
* Should deauthenticated players fall if the login mid-air?
*/
public boolean allowFalling = false;
/**
* Whether to tp player to spawn when joining (to hide original player coordinates)
*/
@ -125,7 +120,7 @@ public class AuthConfig {
public String timeExpired = "§cTime for authentication has expired.";
public String registerRequired = "§6Type /register <password> <password> to claim this account.";
public String alreadyRegistered = "§6This account name is already registered!";
public String registerSuccess = "§aYou are now authenticated.";
public String registerSuccess = "§aAccount was created.";
public String userdataDeleted = "§aUserdata deleted.";
public String userdataUpdated = "§aUserdata updated.";
public String accountDeleted = "§aYour account was successfully deleted!";
@ -137,6 +132,7 @@ public class AuthConfig {
public String worldSpawnSet = "§aSpawn for logging in was set successfully.";
public String corruptedPlayerData = "§cYour data is probably corrupted. Please contact admin.";
public String userNotRegistered = "§cThis player is not registered!";
public String cannotLogout = "§cYou cannot logout!";
}
public static class ExperimentalConfig {
/**

View File

@ -1,16 +1,14 @@
package org.samo_lego.simpleauth.storage;
import com.google.gson.*;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
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;
@ -50,8 +48,6 @@ public class PlayerCache {
/**
* Player stats before de-authentication.
*/
public int lastAir;
public boolean wasOnFire;
public boolean wasInPortal;
/**
@ -78,9 +74,6 @@ public class PlayerCache {
logInfo("Creating cache for " + player.getName().asString());
this.lastIp = player.getIp();
this.lastAir = player.getAir();
this.wasOnFire = player.isOnFire();
// Setting position cache
this.lastLocation.dimension = player.getServerWorld();
this.lastLocation.position = player.getPos();
@ -90,9 +83,7 @@ public class PlayerCache {
this.wasInPortal = player.getBlockState().getBlock().equals(Blocks.NETHER_PORTAL);
}
else {
this.wasOnFire = false;
this.wasInPortal = false;
this.lastAir = 300;
}
String data = DB.getData(uuid);
@ -114,32 +105,6 @@ public class PlayerCache {
this.password = passwordElement.getAsString();
this.isRegistered = !this.password.isEmpty();
}
// 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);
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) {
// Player didn't have any coords in db to tp to
}
}
}
else {
this.isRegistered = false;

View File

@ -7,6 +7,7 @@
"MixinPlayerEntity",
"MixinPlayerManager",
"MixinServerLoginNetworkHandler",
"MixinServerPlayerEntity",
"MixinServerPlayNetworkHandler",
"MixinSlot",
"MixinWorldSaveHandler"