Fixed NPE that could occur when stopping server.

This commit is contained in:
samo_lego 2020-04-16 14:26:27 +02:00
parent 898ed03e26
commit 27cdcf324d
8 changed files with 97 additions and 57 deletions

View File

@ -5,6 +5,7 @@ import net.fabricmc.fabric.api.event.player.*;
import net.fabricmc.fabric.api.event.server.ServerStopCallback;
import net.fabricmc.fabric.api.registry.CommandRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
@ -12,10 +13,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.samo_lego.simpleauth.commands.*;
import org.samo_lego.simpleauth.event.AuthEventHandler;
import org.samo_lego.simpleauth.event.entity.player.ChatCallback;
import org.samo_lego.simpleauth.event.entity.player.PlayerJoinServerCallback;
import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback;
import org.samo_lego.simpleauth.event.entity.player.PlayerMoveCallback;
import org.samo_lego.simpleauth.event.entity.player.*;
import org.samo_lego.simpleauth.event.item.DropItemCallback;
import org.samo_lego.simpleauth.event.item.TakeItemCallback;
import org.samo_lego.simpleauth.storage.AuthConfig;
@ -75,6 +73,7 @@ public class SimpleAuth implements DedicatedServerModInitializer {
});
// Registering the events
PrePlayerJoinCallback.EVENT.register(AuthEventHandler::checkCanPlayerJoinServer);
PlayerJoinServerCallback.EVENT.register(AuthEventHandler::onPlayerJoin);
PlayerLeaveServerCallback.EVENT.register(AuthEventHandler::onPlayerLeave);
DropItemCallback.EVENT.register(AuthEventHandler::onDropItem);
@ -115,6 +114,8 @@ public class SimpleAuth implements DedicatedServerModInitializer {
// De-authenticates player
public static void deauthenticatePlayer(ServerPlayerEntity player) {
if(db.isClosed())
return;
// Marking player as not authenticated, (re)setting login tries to zero
String uuid = player.getUuidAsString();
SimpleAuth.deauthenticatedUsers.put(uuid, new PlayerCache(uuid, player.getIp()));

View File

@ -99,7 +99,7 @@ public class AuthCommand {
SimpleAuth.config.save(new File("./mods/SimpleAuth/config.json"));
if(sender != null)
sender.sendSystemMessage(globalPasswordSet);
((PlayerEntity) sender).sendMessage(globalPasswordSet, false);
else
LOGGER.info(SimpleAuth.config.lang.globalPasswordSet);
return 1;

View File

@ -1,10 +1,12 @@
package org.samo_lego.simpleauth.event;
import com.mojang.authlib.GameProfile;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
@ -14,6 +16,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.samo_lego.simpleauth.storage.PlayerCache;
import java.net.SocketAddress;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -35,18 +38,40 @@ public class AuthEventHandler {
private static Text successfulPortalRescue = new LiteralText(config.lang.successfulPortalRescue);
// Player joining the server
public static void onPlayerJoin(ServerPlayerEntity player) {
// Player pre-join
// Returns text as a reason for disconnect or null to pass
public static LiteralText checkCanPlayerJoinServer(SocketAddress socketAddress, GameProfile profile, PlayerManager manager) {
// Getting the player
String incomingPlayerUsername = profile.getName();
PlayerEntity onlinePlayer = manager.getPlayer(incomingPlayerUsername);
// Checking if player username is valid
String regex = config.main.usernameRegex;
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(player.getName().getString());
if (!matcher.matches()) {
player.networkHandler.disconnect(new LiteralText(String.format(config.lang.disallowedUsername, regex)));
return;
Matcher matcher = pattern.matcher(incomingPlayerUsername);
if(onlinePlayer != null && config.experimental.disableAnotherLocationKick) {
// Player needs to be kicked, since there's already a player with that name
// playing on the server
return new LiteralText(
String.format(
config.lang.playerAlreadyOnline, onlinePlayer.getName().asString()
)
);
}
else if(!matcher.matches()) {
return new LiteralText(
String.format(
config.lang.disallowedUsername, regex
)
);
}
return null;
}
// Player joining the server
public static void onPlayerJoin(ServerPlayerEntity player) {
// Checking if session is still valid
String uuid = player.getUuidAsString();
PlayerCache playerCache = deauthenticatedUsers.getOrDefault(uuid, null);
@ -143,8 +168,8 @@ public class AuthEventHandler {
// Setting that player was actually authenticated before leaving
PlayerCache playerCache = deauthenticatedUsers.get(player.getUuidAsString());
playerCache.wasAuthenticated = true;
// Setting the session expire time
playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000;
}
// Player chatting

View File

@ -0,0 +1,28 @@
package org.samo_lego.simpleauth.event.entity.player;
import com.mojang.authlib.GameProfile;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.server.PlayerManager;
import net.minecraft.text.LiteralText;
import java.net.SocketAddress;
public interface PrePlayerJoinCallback {
Event<PrePlayerJoinCallback> EVENT = EventFactory.createArrayBacked(
PrePlayerJoinCallback.class, listeners -> (
socketAddress, profile, manager
) -> {
for (PrePlayerJoinCallback event : listeners) {
LiteralText returnText = event.checkCanPlayerJoinServer(socketAddress, profile, manager);
if (returnText != null) {
return returnText;
}
}
return null;
});
LiteralText checkCanPlayerJoinServer(SocketAddress socketAddress, GameProfile profile, PlayerManager manager);
}

View File

@ -1,14 +1,24 @@
package org.samo_lego.simpleauth.mixin;
import com.mojang.authlib.GameProfile;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import org.samo_lego.simpleauth.event.entity.player.PlayerJoinServerCallback;
import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback;
import org.samo_lego.simpleauth.event.entity.player.PrePlayerJoinCallback;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.net.SocketAddress;
@Mixin(PlayerManager.class)
public abstract class MixinPlayerManager {
@ -22,4 +32,18 @@ public abstract class MixinPlayerManager {
private void onPlayerLeave(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) {
PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity);
}
// Method for kicking player for
@Inject(method = "checkCanJoin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/text/Text;", at = @At("HEAD"), cancellable = true)
private void checkCanJoin(SocketAddress socketAddress, GameProfile profile, CallbackInfoReturnable<Text> cir) {
// Getting the player that is trying to join the server
PlayerManager manager = (PlayerManager) (Object) this;
LiteralText returnText = PrePlayerJoinCallback.EVENT.invoker().checkCanPlayerJoinServer(socketAddress, profile, manager);
if(returnText != null) {
// Canceling player joining with the returnText message
cir.setReturnValue(returnText);
}
}
}

View File

@ -1,43 +0,0 @@
package org.samo_lego.simpleauth.mixin;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerLoginNetworkHandler;
import net.minecraft.text.LiteralText;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static org.samo_lego.simpleauth.SimpleAuth.config;
@Mixin(ServerLoginNetworkHandler.class)
public abstract class MixinServerLoginNetworkHandler {
@Shadow @Final
private MinecraftServer server;
@Shadow
private GameProfile profile;
@Inject(method = "acceptPlayer()V", at = @At("HEAD"), cancellable = true)
private void acceptPlayer(CallbackInfo ci) {
// Player pre-join event, we don't do standard callback, since
// there are lots of variables that would need to be passed over
PlayerEntity onlinePlayer = this.server.getPlayerManager().getPlayer(this.profile.getName());
// Getting network handler
ServerLoginNetworkHandler handler = (ServerLoginNetworkHandler) (Object) this;
if (config.experimental.disableAnotherLocationKick && onlinePlayer != null) {
// Player needs to be kicked, since there's already a player with that name
// playing on the server
handler.disconnect(new LiteralText(String.format(config.lang.playerAlreadyOnline, onlinePlayer.getName().asString())));
ci.cancel();
}
}
}

View File

@ -39,6 +39,12 @@ public class SimpleAuthDatabase {
}
}
// Tells whether db connection is closed
public boolean isClosed() {
return levelDBStore != null;
}
// When player registers, we insert the data into DB
public boolean registerUser(String uuid, String data) {
try {

View File

@ -7,7 +7,6 @@
"server": [
"MixinPlayerEntity",
"MixinPlayerManager",
"MixinServerLoginNetworkHandler",
"MixinServerPlayNetworkHandler",
"MixinSlot"
],