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

View File

@ -1,10 +1,12 @@
package org.samo_lego.simpleauth.event; package org.samo_lego.simpleauth.event;
import com.mojang.authlib.GameProfile;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
@ -14,6 +16,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.samo_lego.simpleauth.storage.PlayerCache; import org.samo_lego.simpleauth.storage.PlayerCache;
import java.net.SocketAddress;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -35,18 +38,40 @@ public class AuthEventHandler {
private static Text successfulPortalRescue = new LiteralText(config.lang.successfulPortalRescue); private static Text successfulPortalRescue = new LiteralText(config.lang.successfulPortalRescue);
// Player joining the server // Player pre-join
public static void onPlayerJoin(ServerPlayerEntity player) { // 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 // Checking if player username is valid
String regex = config.main.usernameRegex; String regex = config.main.usernameRegex;
Pattern pattern = Pattern.compile(regex); Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(player.getName().getString()); Matcher matcher = pattern.matcher(incomingPlayerUsername);
if (!matcher.matches()) {
player.networkHandler.disconnect(new LiteralText(String.format(config.lang.disallowedUsername, regex)));
return;
}
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 // Checking if session is still valid
String uuid = player.getUuidAsString(); String uuid = player.getUuidAsString();
PlayerCache playerCache = deauthenticatedUsers.getOrDefault(uuid, null); PlayerCache playerCache = deauthenticatedUsers.getOrDefault(uuid, null);
@ -143,8 +168,8 @@ public class AuthEventHandler {
// Setting that player was actually authenticated before leaving // Setting that player was actually authenticated before leaving
PlayerCache playerCache = deauthenticatedUsers.get(player.getUuidAsString()); PlayerCache playerCache = deauthenticatedUsers.get(player.getUuidAsString());
playerCache.wasAuthenticated = true; playerCache.wasAuthenticated = true;
// Setting the session expire time
playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000; playerCache.validUntil = System.currentTimeMillis() + config.main.sessionTimeoutTime * 1000;
} }
// Player chatting // 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; package org.samo_lego.simpleauth.mixin;
import com.mojang.authlib.GameProfile;
import net.minecraft.network.ClientConnection; import net.minecraft.network.ClientConnection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager; import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity; 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.PlayerJoinServerCallback;
import org.samo_lego.simpleauth.event.entity.player.PlayerLeaveServerCallback; 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.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.net.SocketAddress;
@Mixin(PlayerManager.class) @Mixin(PlayerManager.class)
public abstract class MixinPlayerManager { public abstract class MixinPlayerManager {
@ -22,4 +32,18 @@ public abstract class MixinPlayerManager {
private void onPlayerLeave(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) { private void onPlayerLeave(ServerPlayerEntity serverPlayerEntity, CallbackInfo ci) {
PlayerLeaveServerCallback.EVENT.invoker().onPlayerLeave(serverPlayerEntity); 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 // When player registers, we insert the data into DB
public boolean registerUser(String uuid, String data) { public boolean registerUser(String uuid, String data) {
try { try {

View File

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