Files
Purpur/patches/server/0010-AFK-API.patch
2023-06-08 19:58:32 -07:00

333 lines
19 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Thu, 8 Aug 2019 15:29:15 -0500
Subject: [PATCH] AFK API
Adds the option for display names to be used in the afk broadcast
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index dfdc50093e5d4b60438acfc12ab9f14255fa7a99..3b6af7b76db3d550eb7f199f9e8afd1c0af97e2e 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -2116,8 +2116,68 @@ public class ServerPlayer extends Player {
public void resetLastActionTime() {
this.lastActionTime = Util.getMillis();
+ this.setAfk(false); // Purpur
}
+ // Purpur Start
+ private boolean isAfk = false;
+
+ @Override
+ public void setAfk(boolean afk) {
+ if (this.isAfk == afk) {
+ return;
+ }
+
+ String msg = afk ? org.purpurmc.purpur.PurpurConfig.afkBroadcastAway : org.purpurmc.purpur.PurpurConfig.afkBroadcastBack;
+
+ org.purpurmc.purpur.event.PlayerAFKEvent event = new org.purpurmc.purpur.event.PlayerAFKEvent(this.getBukkitEntity(), afk, this.level.purpurConfig.idleTimeoutKick, msg, !Bukkit.isPrimaryThread());
+ if (!event.callEvent() || event.shouldKick()) {
+ return;
+ }
+
+ this.isAfk = afk;
+
+ if (!afk) {
+ resetLastActionTime();
+ }
+
+ msg = event.getBroadcastMsg();
+ if (msg != null && !msg.isEmpty()) {
+ String playerName = this.getGameProfile().getName();
+ if (org.purpurmc.purpur.PurpurConfig.afkBroadcastUseDisplayName) {
+ net.kyori.adventure.text.Component playerDisplayNameComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(this.getBukkitEntity().getDisplayName());
+ playerName = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer.plainText().serialize(playerDisplayNameComponent);
+ }
+ server.getPlayerList().broadcastMiniMessage(String.format(msg, playerName), false);
+ }
+
+ if (this.level.purpurConfig.idleTimeoutUpdateTabList) {
+ String scoreboardName = getScoreboardName();
+ String playerListName = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().serialize(getBukkitEntity().playerListName());
+ String[] split = playerListName.split(scoreboardName);
+ String prefix = (split.length > 0 ? split[0] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix, "");
+ String suffix = (split.length > 1 ? split[1] : "").replace(org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, "");
+ if (afk) {
+ getBukkitEntity().setPlayerListName(org.purpurmc.purpur.PurpurConfig.afkTabListPrefix + prefix + scoreboardName + suffix + org.purpurmc.purpur.PurpurConfig.afkTabListSuffix, true);
+ } else {
+ getBukkitEntity().setPlayerListName(prefix + scoreboardName + suffix);
+ }
+ }
+
+ ((ServerLevel) this.level).updateSleepingPlayerList();
+ }
+
+ @Override
+ public boolean isAfk() {
+ return this.isAfk;
+ }
+
+ @Override
+ public boolean canBeCollidedWith() {
+ return !this.isAfk() && super.canBeCollidedWith();
+ }
+ // Purpur End
+
public ServerStatsCounter getStats() {
return this.stats;
}
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index a419308c2d71bf0e228aa7c591deb76c77eda894..1645c63f36c6d4bcd6e6ac474af8504848b1c077 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -340,6 +340,20 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
private boolean justTeleported = false;
private boolean hasMoved; // Spigot
+ // Purpur start
+ private final com.google.common.cache.LoadingCache<CraftPlayer, Boolean> kickPermissionCache = com.google.common.cache.CacheBuilder.newBuilder()
+ .maximumSize(1000)
+ .expireAfterWrite(1, java.util.concurrent.TimeUnit.MINUTES)
+ .build(
+ new com.google.common.cache.CacheLoader<>() {
+ @Override
+ public Boolean load(CraftPlayer player) {
+ return player.hasPermission("purpur.bypassIdleKick");
+ }
+ }
+ );
+ // Purpur end
+
public CraftPlayer getCraftPlayer() {
return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
}
@@ -433,6 +447,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
}
if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits.
+ // Purpur start
+ this.player.setAfk(true);
+ if (!this.player.level.purpurConfig.idleTimeoutKick || (!Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK")) && kickPermissionCache.getUnchecked(this.player.getBukkitEntity()))) {
+ return;
+ }
+ // Purpur end
this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
}
@@ -751,6 +771,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
this.lastYaw = to.getYaw();
this.lastPitch = to.getPitch();
+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur
+
// Skip the first time we do this
if (true) { // Spigot - don't skip any move events
Location oldTo = to.clone();
@@ -1549,7 +1571,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
if (!this.player.isChangingDimension() && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot
flag2 = true; // Paper - diff on change, this should be moved wrongly
- ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
+ ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur
}
// Paper start - optimise out extra getCubes
@@ -1596,6 +1618,8 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
this.lastYaw = to.getYaw();
this.lastPitch = to.getPitch();
+ if (!to.getWorld().getUID().equals(from.getWorld().getUID()) || to.getBlockX() != from.getBlockX() || to.getBlockY() != from.getBlockY() || to.getBlockZ() != from.getBlockZ() || to.getYaw() != from.getYaw() || to.getPitch() != from.getPitch()) this.player.resetLastActionTime(); // Purpur
+
// Skip the first time we do this
if (from.getX() != Double.MAX_VALUE) {
Location oldTo = to.clone();
diff --git a/src/main/java/net/minecraft/server/players/SleepStatus.java b/src/main/java/net/minecraft/server/players/SleepStatus.java
index 823efad652d8ff9e96b99375b102fef6f017716e..60f89d7c77a5e792e21e93e35ed1670bd565799a 100644
--- a/src/main/java/net/minecraft/server/players/SleepStatus.java
+++ b/src/main/java/net/minecraft/server/players/SleepStatus.java
@@ -19,7 +19,7 @@ public class SleepStatus {
public boolean areEnoughDeepSleeping(int percentage, List<ServerPlayer> players) {
// CraftBukkit start
- int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping; }).count();
+ int j = (int) players.stream().filter((eh) -> { return eh.isSleepingLongEnough() || eh.fauxSleeping || (eh.level.purpurConfig.idleTimeoutCountAsSleeping && eh.isAfk()); }).count(); // Purpur
boolean anyDeepSleep = players.stream().anyMatch(Player::isSleepingLongEnough);
return anyDeepSleep && j >= this.sleepersNeeded(percentage);
@@ -52,7 +52,7 @@ public class SleepStatus {
if (!entityplayer.isSpectator()) {
++this.activePlayers;
- if (entityplayer.isSleeping() || entityplayer.fauxSleeping) { // CraftBukkit
+ if ((entityplayer.isSleeping() || entityplayer.fauxSleeping) || (entityplayer.level.purpurConfig.idleTimeoutCountAsSleeping && entityplayer.isAfk())) { // CraftBukkit // Purpur
++this.sleepingPlayers;
}
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java
index 3ff999734d14e2b6e7828e117f5ee32a60c26bc1..cfa9607241c3e69777ffc317206996c2f783437a 100644
--- a/src/main/java/net/minecraft/world/entity/EntitySelector.java
+++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java
@@ -39,6 +39,7 @@ public final class EntitySelector {
return net.minecraft.util.Mth.clamp(serverPlayer.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= playerInsomniaTicks;
};
// Paper end
+ public static Predicate<Player> notAfk = (player) -> !player.isAfk(); // Purpur
private EntitySelector() {}
// Paper start
diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
index 58422f00c7d64dbd1cf6d7211c9838875cbe7778..12efea4fd2a78f32a165941aff52edd024a5592b 100644
--- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
@@ -64,6 +64,10 @@ public class TargetingConditions {
return false;
} else if (this.selector != null && !this.selector.test(targetEntity)) {
return false;
+ // Purpur start
+ } else if (!targetEntity.level.purpurConfig.idleTimeoutTargetPlayer && targetEntity instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) {
+ return false;
+ // Purpur end
} else {
if (baseEntity == null) {
if (this.isCombat && (!targetEntity.canBeSeenAsEnemy() || targetEntity.level().getDifficulty() == Difficulty.PEACEFUL)) {
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index 96726f06b83193fd7c5156efe6411b10eb1db435..7b39cb5346925c14f3f144d622ca7e5855420aa6 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -193,6 +193,13 @@ public abstract class Player extends LivingEntity {
public boolean fauxSleeping;
public int oldLevel = -1;
+ public void setAfk(boolean afk) {
+ }
+
+ public boolean isAfk() {
+ return false;
+ }
+
@Override
public CraftHumanEntity getBukkitEntity() {
return (CraftHumanEntity) super.getBukkitEntity();
diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
index 3b959f42d958bf0f426853aee56753d6c455fcdb..d17abb283ea818244df0379d6b57fc634071e0b9 100644
--- a/src/main/java/net/minecraft/world/level/EntityGetter.java
+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
@@ -154,7 +154,7 @@ public interface EntityGetter {
default boolean hasNearbyAlivePlayer(double x, double y, double z, double range) {
for(Player player : this.players()) {
- if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
+ if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player) && EntitySelector.notAfk.test(player)) {
double d = player.distanceToSqr(x, y, z);
if (range < 0.0D || d < range * range) {
return true;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 09d2f1586d19d0d5c9c78cde5263f30e7a7d0647..fea8f974c5e35b0f5c515a4c7f033482e54979c6 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -501,10 +501,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void setPlayerListName(String name) {
+ // Purpur start
+ setPlayerListName(name, false);
+ }
+ public void setPlayerListName(String name, boolean useMM) {
+ // Purpur end
if (name == null) {
name = getName();
}
- this.getHandle().listName = name.equals(getName()) ? null : CraftChatMessage.fromStringOrNull(name);
+ this.getHandle().listName = name.equals(getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur
for (ServerPlayer player : (List<ServerPlayer>) server.getHandle().players) {
if (player.getBukkitEntity().canSee(this)) {
player.connection.send(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, this.getHandle()));
@@ -3149,5 +3154,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public boolean usesPurpurClient() {
return getHandle().purpurClient;
}
+
+ @Override
+ public boolean isAfk() {
+ return getHandle().isAfk();
+ }
+
+ @Override
+ public void setAfk(boolean setAfk) {
+ getHandle().setAfk(setAfk);
+ }
+
+ @Override
+ public void resetIdleTimer() {
+ getHandle().resetLastActionTime();
+ }
// Purpur end
}
diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
index 687710a138edd8492e58aa026872851d00466508..d11442476371fefe6549654c97825a01d2cac61e 100644
--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java
+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
@@ -173,6 +173,19 @@ public class PurpurConfig {
return builder.build();
}
+ public static String afkBroadcastAway = "<yellow><italic>%s is now AFK";
+ public static String afkBroadcastBack = "<yellow><italic>%s is no longer AFK";
+ public static boolean afkBroadcastUseDisplayName = false;
+ public static String afkTabListPrefix = "[AFK] ";
+ public static String afkTabListSuffix = "";
+ private static void messages() {
+ afkBroadcastAway = getString("settings.messages.afk-broadcast-away", afkBroadcastAway);
+ afkBroadcastBack = getString("settings.messages.afk-broadcast-back", afkBroadcastBack);
+ afkBroadcastUseDisplayName = getBoolean("settings.messages.afk-broadcast-use-display-name", afkBroadcastUseDisplayName);
+ afkTabListPrefix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-prefix", afkTabListPrefix)));
+ afkTabListSuffix = MiniMessage.miniMessage().serialize(MiniMessage.miniMessage().deserialize(getString("settings.messages.afk-tab-list-suffix", afkTabListSuffix)));
+ }
+
public static int barrelRows = 3;
public static boolean enderChestSixRows = false;
public static boolean enderChestPermissionRows = false;
diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
index f004e4290c0711d2e04c05677981f31b463af9fe..aa2371a611ca83247d422cc79ea37a8d798fdd0f 100644
--- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
@@ -92,6 +92,24 @@ public class PurpurWorldConfig {
return value.isEmpty() ? fallback : value;
}
+ public boolean idleTimeoutKick = true;
+ public boolean idleTimeoutTickNearbyEntities = true;
+ public boolean idleTimeoutCountAsSleeping = false;
+ public boolean idleTimeoutUpdateTabList = false;
+ public boolean idleTimeoutTargetPlayer = true;
+ private void playerSettings() {
+ if (PurpurConfig.version < 19) {
+ boolean oldVal = getBoolean("gameplay-mechanics.player.idle-timeout.mods-target", idleTimeoutTargetPlayer);
+ set("gameplay-mechanics.player.idle-timeout.mods-target", null);
+ set("gameplay-mechanics.player.idle-timeout.mobs-target", oldVal);
+ }
+ idleTimeoutKick = System.getenv("PURPUR_FORCE_IDLE_KICK") == null ? getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick) : Boolean.parseBoolean(System.getenv("PURPUR_FORCE_IDLE_KICK"));
+ idleTimeoutTickNearbyEntities = getBoolean("gameplay-mechanics.player.idle-timeout.tick-nearby-entities", idleTimeoutTickNearbyEntities);
+ idleTimeoutCountAsSleeping = getBoolean("gameplay-mechanics.player.idle-timeout.count-as-sleeping", idleTimeoutCountAsSleeping);
+ idleTimeoutUpdateTabList = getBoolean("gameplay-mechanics.player.idle-timeout.update-tab-list", idleTimeoutUpdateTabList);
+ idleTimeoutTargetPlayer = getBoolean("gameplay-mechanics.player.idle-timeout.mobs-target", idleTimeoutTargetPlayer);
+ }
+
public double axolotlMaxHealth = 14.0D;
private void axolotlSettings() {
axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth);
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index d4da9ec6e00bb92b70598ee9a0d0ca5816562378..ea978171f89ac467b235f66fd539b957ba73e371 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -199,6 +199,7 @@ public class ActivationRange
continue;
}
+ if (!player.level.purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur
// Paper start
int worldHeight = world.getHeight();
ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );