package dev.micle.loginprotection.data; import dev.micle.loginprotection.LoginProtection; import dev.micle.loginprotection.network.NetworkManager; import dev.micle.loginprotection.network.server.PlayerStatePacket; import dev.micle.loginprotection.network.server.RequestLastInputTickPacket; import dev.micle.loginprotection.setup.Config; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkDirection; import java.util.ArrayList; import java.util.List; import java.util.TimerTask; import java.util.UUID; public class ProtectedPlayerManager { // Initialize variables private static final List protectedPlayers = new ArrayList<>(); /** * Method for initializing the ProtectedPlayerManager. */ public static void init() { for (ProtectedPlayer protectedPlayer : protectedPlayers) { removePlayer(protectedPlayer.getPlayerUUID()); } } /** * Adds a player to be protected. * @param playerUUID UUID of player to protect. */ public static void addPlayer(UUID playerUUID) { if (getPlayer(playerUUID) != null) { return; } try { protectedPlayers.add(new ProtectedPlayer(playerUUID, ProtectedPlayer.State.JOINING)); NetworkManager.getChannel().sendTo(new PlayerStatePacket(ProtectedPlayer.State.JOINING), LoginProtection.getProxy().getServer().getPlayerList().getPlayer(playerUUID).connection.getConnection(), NetworkDirection.PLAY_TO_CLIENT); } catch (NullPointerException ignored) {} } /** * Gets a protected player from the list of protected players. * @param playerUUID UUID of player to get. * @return ProtectedPlayer instance if player is present, otherwise null. */ public static ProtectedPlayer getPlayer(UUID playerUUID) { for (ProtectedPlayer protectedPlayer : protectedPlayers) { if (protectedPlayer.getPlayerUUID().equals(playerUUID)) { return protectedPlayer; } } return null; } /** * Removes a player from the list of protected players. * @param playerUUID UUID of player to remove. */ public static void removePlayer(UUID playerUUID) { ProtectedPlayer player = getPlayer(playerUUID); if (player == null) { return; } player.getTimer().cancel(); protectedPlayers.remove(player); } /** * Updates a player's state appropriately. * @param playerUUID UUID of player to update the state of. */ public static void updateState(UUID playerUUID) { ProtectedPlayer protectedPlayer = getPlayer(playerUUID); ServerPlayer player = LoginProtection.getProxy().getServer().getPlayerList().getPlayer(playerUUID); if (player == null) { removePlayer(playerUUID); return; } if (protectedPlayer == null) { addPlayer(playerUUID); } else { ProtectedPlayer.State currentState = protectedPlayer.getState(); if (currentState.equals(ProtectedPlayer.State.JOINING) || currentState.equals(ProtectedPlayer.State.AFK)) { // JOINING, AFK -> ACTIVE protectedPlayer.setState(ProtectedPlayer.State.ACTIVE); if (Config.Server.POST_GRACE_ENABLED.get()) { startGracePeriod(playerUUID); } else { applyPostEffects(playerUUID); } startAfkTimer(playerUUID, Config.Server.AFK_TIME_THRESHOLD.get() * 1000); } else { // ACTIVE -> AFK protectedPlayer.setState(ProtectedPlayer.State.AFK); } // Send state packet to player NetworkManager.getChannel().sendTo(new PlayerStatePacket(protectedPlayer.getState()), player.connection.getConnection(), NetworkDirection.PLAY_TO_CLIENT); } } public static void startAfkTimer(UUID playerUUID, long delay) { ProtectedPlayer player = getPlayer(playerUUID); if (player == null) { return; } // Create scheduled task // Todo: Make task period change according to the last input tick of a player player.setAfkTimerTask(new TimerTask() { @Override public void run() { if (!player.getState().equals(ProtectedPlayer.State.ACTIVE)) { this.cancel(); return; } try { // Send request for list input tick packet to player NetworkManager.getChannel().sendTo(new RequestLastInputTickPacket(), LoginProtection.getProxy().getServer().getPlayerList().getPlayer(playerUUID).connection.getConnection(), NetworkDirection.PLAY_TO_CLIENT); } catch (NullPointerException e) { removePlayer(playerUUID); } } }, delay); } /** * Starts the grace period counter for a player. * @param playerUUID UUID of player to start the grace period for. */ private static void startGracePeriod(UUID playerUUID) { ProtectedPlayer player = getPlayer(playerUUID); if (player == null) { return; } // Set grace period length player.setGracePeriod(Config.Server.POST_GRACE_DURATION.get()); // Create scheduled task player.setGracePeriodTimerTask(new TimerTask() { @Override public void run() { player.setGracePeriod(player.getGracePeriodTimeRemaining() - 1); if (player.getGracePeriodTimeRemaining() == 0) { applyPostEffects(playerUUID); this.cancel(); if (!Config.Server.AFK_PROTECTION_ENABLED.get()) { ProtectedPlayerManager.removePlayer(playerUUID); } } } }); } /** * Applies effects to the player. * @param playerUUID UUID of player to apply effects to. */ private static void applyPostEffects(UUID playerUUID) { // Get player entity Player player = LoginProtection.getProxy().getServer().getPlayerList().getPlayer(playerUUID); if (player == null) { removePlayer(playerUUID); return; } // Apply effects if (player.isInWater()) { if (Config.Server.POST_REFILL_AIR_ENABLED.get()) { player.setAirSupply(player.getMaxAirSupply()); } if (Config.Server.POST_WATER_ENABLED.get()) { player.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, Config.Server.POST_WATER_DURATION.get() * 20, 0)); } } if (player.isInLava()) { if (Config.Server.POST_LAVA_ENABLED.get()) { player.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, Config.Server.POST_LAVA_DURATION.get()*20, 0)); } } if (player.isOnFire()) { if (Config.Server.POST_FIRE_ENABLED.get()) { player.addEffect(new MobEffectInstance(MobEffects.FIRE_RESISTANCE, Config.Server.POST_FIRE_DURATION.get()*20, 0)); } } // Remove player if afk protection is disabled if (!Config.Server.AFK_PROTECTION_ENABLED.get()) { ProtectedPlayerManager.removePlayer(playerUUID); } } }