- Fix rare NPE occurs when players disconnect while KB is being processed or a command is being run

Fabric:
- Check if native player object is null before constructing PlatformPlayer
This commit is contained in:
Axionize committed 2025-04-01 05:25:43 -04:00
1 parent 622c23a729
commit 35faf08cfa
11 files changed
+58 -35

No files matched your search

@@ -78,7 +78,6 @@ public class BukkitPlayer implements PlatformPlayer {
Preconditions.checkArgument(player != null);
this.bukkitPlayer = player;
this.user = PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer);
Preconditions.checkArgument(user != null);
}
@Override
@@ -220,7 +219,7 @@ public class BukkitPlayer implements PlatformPlayer {
}
@Override
public User getUser() {
public @Nullable User getUser() {
return this.user;
}
@@ -1,11 +1,13 @@
package me.caseload.knockbacksync.command.subcommand;
import com.github.retrooper.packetevents.protocol.player.User;
import me.caseload.knockbacksync.Base;
import me.caseload.knockbacksync.ConfigWrapper;
import me.caseload.knockbacksync.command.generic.BuilderCommand;
import me.caseload.knockbacksync.command.generic.PlayerSelector;
import me.caseload.knockbacksync.event.KBSyncEventHandler;
import me.caseload.knockbacksync.event.events.ConfigReloadEvent;
import me.caseload.knockbacksync.manager.ConfigManager;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.sender.Sender;
@@ -18,6 +20,8 @@ import java.util.function.Predicate;
public class PingCommand implements BuilderCommand {
private static final ConfigManager configManager = Base.INSTANCE.getConfigManager();
private String pingSelfAvailableMessage;
private String pingSelfUnavailableMessage;
private String pingOtherAvailableMessage;
@@ -51,17 +55,17 @@ public class PingCommand implements BuilderCommand {
if (context.sender().isConsole()) {
context.sender().sendMessage(ChatUtil.translateAlternateColorCodes('&', mustSpecifyPlayerFromConsoleMessage));
} else {
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), null));
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), context.sender().getName(), null, null));
}
} else {
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), targetSelector.getSinglePlayer().getUUID()));
context.sender().sendMessage(getPingMessage(context.sender().getUniqueId(), context.sender().getName(), targetSelector.getSinglePlayer().getUUID(), targetSelector.getSinglePlayer().getName()));
}
})
);
Base.INSTANCE.getEventBus().registerListeners(this);
}
private void loadConfig() {
ConfigWrapper configWrapper = Base.INSTANCE.getConfigManager().getConfigWrapper();
ConfigWrapper configWrapper = configManager.getConfigWrapper();
pingSelfAvailableMessage = configWrapper.getString("messages.ping.self.available",
"Your real ping is &b%ping%&rms. Jitter: &b%jitter%&rms. Spike: &b%spike%&r. Compensated ping: &b%compensated%&rms.");
pingSelfUnavailableMessage = configWrapper.getString("messages.ping.self.unavailable",
@@ -78,26 +82,27 @@ public class PingCommand implements BuilderCommand {
"&cKnockback synchronization is currently disabled for the target player.");
}
private String getPingMessage(UUID senderUUID, UUID targetUUID) {
PlayerData playerData = null;
private String getPingMessage(UUID senderUUID, String senderName, UUID targetUUID, String targetName) {
boolean isSelf = senderUUID.equals(targetUUID) || targetUUID == null;
if (senderUUID == null) {
senderUUID = targetUUID;
}
String message;
if (isSelf) {
playerData = PlayerDataManager.getPlayerData(Base.INSTANCE.getPlatformServer().getPlayer(senderUUID).getUser());
if (playerData == null) {
return ChatUtil.translateAlternateColorCodes('&', knockbacksyncDisabledForYouMessage);
}
message = knockbacksyncDisabledForYouMessage;
targetUUID = senderUUID;
targetName = senderName;
} else {
playerData = PlayerDataManager.getPlayerData(Base.INSTANCE.getPlatformServer().getPlayer(targetUUID).getUser());
if (playerData == null) {
return ChatUtil.translateAlternateColorCodes('&', knockbacksyncDisabledForTargetMessage);
}
message = knockbacksyncDisabledForTargetMessage;
}
User targetUser = Base.INSTANCE.getPlatformServer().getPlayer(targetUUID).getUser();
if (targetUser == null)
return ChatUtil.translateAlternateColorCodes('&',
configManager.getPlayerDisconnectedWhileExecutingCommand()
).replace("%player%", targetName);
PlayerData playerData = PlayerDataManager.getPlayerData(targetUser);
if (playerData == null)
return ChatUtil.translateAlternateColorCodes('&', message);
String rawReturnString;
if (playerData.getPing() == null) {
rawReturnString = isSelf ? pingSelfUnavailableMessage : pingOtherUnavailableMessage;
@@ -14,7 +14,7 @@ import java.util.function.Predicate;
public class ReloadCommand implements BuilderCommand {
private final ConfigManager configManager = Base.INSTANCE.getConfigManager();
private static final ConfigManager configManager = Base.INSTANCE.getConfigManager();
private String rawReloadMessage = configManager.getConfigWrapper().getString("messages.reload.success", "&aSuccessfully reloaded KnockbackSync.");
public void register(CommandManager<Sender> manager) {
@@ -14,7 +14,6 @@ import me.caseload.knockbacksync.util.ChatUtil;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.permission.PredicatePermission;
import java.util.UUID;
import java.util.function.Predicate;
public class StatusCommand implements BuilderCommand {
@@ -85,6 +84,13 @@ public class StatusCommand implements BuilderCommand {
}
private void showPlayerStatus(Sender sender, PlatformPlayer target) {
if (target.getUser() == null) {
String message = ChatUtil.translateAlternateColorCodes('&',
configManager.getPlayerDisconnectedWhileExecutingCommand()
).replace("%player%", target.getName());
sender.sendMessage(message);
}
boolean globalStatus = configManager.isToggled();
boolean playerStatus = PlayerDataManager.containsPlayerData(target.getUser());
@@ -1,5 +1,4 @@
package me.caseload.knockbacksync.command.subcommand;
import com.github.retrooper.packetevents.protocol.player.Combat;
import com.github.retrooper.packetevents.protocol.player.User;
import me.caseload.knockbacksync.Base;
import me.caseload.knockbacksync.ConfigWrapper;
@@ -11,13 +10,11 @@ import me.caseload.knockbacksync.manager.ConfigManager;
import me.caseload.knockbacksync.manager.PlayerDataManager;
import me.caseload.knockbacksync.permission.PermissionChecker;
import me.caseload.knockbacksync.player.PlatformPlayer;
import me.caseload.knockbacksync.player.PlayerData;
import me.caseload.knockbacksync.sender.Sender;
import me.caseload.knockbacksync.util.ChatUtil;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.permission.PredicatePermission;
import java.util.UUID;
import java.util.function.Predicate;
public class ToggleCommand implements BuilderCommand {
@@ -70,7 +67,7 @@ public class ToggleCommand implements BuilderCommand {
if (targetSelector == null) {
// Global toggle
if (permissionChecker.hasPermission(sender, TOGGLE_GLOBAL_PERMISSION, false)) {
toggleGlobalKnockback(configManager, sender);
toggleGlobalKnockback(sender);
} else {
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', noGlobalPermissionMessage));
}
@@ -88,7 +85,7 @@ public class ToggleCommand implements BuilderCommand {
if (!configManager.isToggled()) {
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', serverDisabledMessage));
} else {
togglePlayerKnockback(target, configManager, sender);
togglePlayerKnockback(target, sender);
}
}
})
@@ -96,7 +93,7 @@ public class ToggleCommand implements BuilderCommand {
}
private static void toggleGlobalKnockback(ConfigManager configManager, Sender sender) {
private static void toggleGlobalKnockback(Sender sender) {
boolean toggledState = !configManager.isToggled();
ToggleOnOffEvent toggleOnOffEvent = new ToggleOnOffEvent(toggledState);
toggleOnOffEvent.post();
@@ -106,8 +103,8 @@ public class ToggleCommand implements BuilderCommand {
toggledState = toggleOnOffEvent.getStatus();
configManager.setToggled(toggledState);
Base.INSTANCE.getConfigManager().getConfigWrapper().set("enabled", toggledState);
Base.INSTANCE.getConfigManager().saveConfig();
configManager.getConfigWrapper().set("enabled", toggledState);
configManager.saveConfig();
String message = ChatUtil.translateAlternateColorCodes('&',
toggledState ? configManager.getEnableMessage() : configManager.getDisableMessage()
@@ -115,8 +112,14 @@ public class ToggleCommand implements BuilderCommand {
sender.sendMessage(ChatUtil.translateAlternateColorCodes('&', message));
}
private static void togglePlayerKnockback(PlatformPlayer target, ConfigManager configManager, Sender sender) {
private static void togglePlayerKnockback(PlatformPlayer target, Sender sender) {
User user = target.getUser();
if (user == null) {
String message = ChatUtil.translateAlternateColorCodes('&',
configManager.getPlayerDisconnectedWhileExecutingCommand()
).replace("%player%", target.getName());
sender.sendMessage(message);
}
if (PlayerDataManager.shouldExempt(target.getUUID())) {
String message = ChatUtil.translateAlternateColorCodes('&',
@@ -12,7 +12,7 @@ public abstract class PlayerDamageListener {
return;
User user = victim.getUser();
if (user == null) return; // Prevent errors with fake players
if (user == null) return; // Prevent errors with players disconnecting while this is running (or with fake player?)
PlayerData playerData = PlayerDataManager.getPlayerData(user);
if (playerData == null)
@@ -16,7 +16,7 @@ public abstract class PlayerKnockbackListener {
return;
User user = victim.getUser();
if (user == null) return; // Prevent errors with fake players
if (user == null) return; // Prevent errors with players disconnecting while this is running (or with fake player?)
PlayerData victimPlayerData = PlayerDataManager.getPlayerData(user);
if (victimPlayerData == null)
@@ -36,6 +36,7 @@ public class ConfigManager {
private String playerEnableMessage;
private String playerDisableMessage;
private String playerIneligibleMessage;
private String playerDisconnectedWhileExecutingCommand;
private AbstractTaskHandle pingTask;
@@ -122,6 +123,7 @@ public class ConfigManager {
playerEnableMessage = configWrapper.getString("messages.toggle.player.enable", "&aSuccessfully enabled KnockbackSync for %player%.");
playerDisableMessage = configWrapper.getString("messages.toggle.player.disable", "&aSuccessfully &cdisabled &aKnockbackSync for %player%.");
playerIneligibleMessage = configWrapper.getString("messages.toggle.player.ineligible", "&c%player% is ineligible for KnockbackSync. If you believe this is in error, please contact your server administrators.");
playerDisconnectedWhileExecutingCommand = configWrapper.getString("messages.toggle.player.disconnected-while-executing-command", "&c%player% disconnected while executing command.");
}
public void updateConfig() {
@@ -52,7 +52,12 @@ public interface PlatformPlayer {
BoundingBox getBoundingBox();
User getUser();
/**
* If a player disconnects while we are running the constructor of a PlatformPlayer
* PacketEvents.getAPI().getPlayerManager().getUser(bukkitPlayer) may return null
* This will make the internal field null and thus this function may return null.
*/
@Nullable User getUser();
void setClientBrand(String brand);
+1
View File
@@ -194,6 +194,7 @@ messages:
enable: "&aSuccessfully enabled KnockbackSync for %player%."
disable: "&aSuccessfully &cdisabled &aKnockbackSync for %player%."
ineligible: "&c%player% is ineligible for KnockbackSync. If you believe this is in error, please contact your server administrators."
disconnected-while-executing-command: "&c%player% disconnected while executing command."
permission:
no_global: "&cYou don't have permission to toggle the global setting."
no_self: "&cYou do not have permission to toggle your knockback."
@@ -4,6 +4,7 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.world.BoundingBox;
import com.github.retrooper.packetevents.util.Vector3d;
import com.google.common.base.Preconditions;
import me.caseload.knockbacksync.world.FabricWorld;
import me.caseload.knockbacksync.world.PlatformWorld;
import net.minecraft.enchantment.Enchantment;
@@ -33,6 +34,7 @@ public class FabricPlayer implements PlatformPlayer {
private String clientBrand = "vanilla";
public FabricPlayer(ServerPlayerEntity player) {
Preconditions.checkArgument(player != null);
this.fabricPlayer = player;
this.user = PacketEvents.getAPI().getPlayerManager().getUser(fabricPlayer);
}
@@ -156,7 +158,7 @@ public class FabricPlayer implements PlatformPlayer {
}
@Override
public User getUser() {
public @Nullable User getUser() {
return user;
}