This commit is contained in:
William Blake Galbreath
2025-01-04 19:40:44 -08:00
committed by granny
parent c5902528c1
commit fb0d9a1cd6
16 changed files with 378 additions and 455 deletions

View File

@@ -42,7 +42,7 @@ index ebeeb63c3dca505a3ce8b88feaa5d2ca20ec24a2..0029717fbd4f2475b07abf4f7036cebb
public LevelChunk getChunkIfLoaded(int x, int z) {
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index dc146170def1b349291916f13164a5f4c43ee6f2..544b87f020ad2177b6ec86ce2d1e49a14be8c2b4 100644
index cbdb3b12e11e9b50978febebd2b275997b86c1b8..c6bd1ec93d40d43c151c68ea8892c33db4511d03 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -838,6 +838,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -62,10 +62,10 @@ index dc146170def1b349291916f13164a5f4c43ee6f2..544b87f020ad2177b6ec86ce2d1e49a1
private void updatePlayerAttributes() {
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index d248671b2e1c6256fc4d74320bdb29ca078bad0b..e2d30d0daae4ad088c723a2d85760c340fe83e9d 100644
index b083228bb3dc87794c6f177ad99832daf6925a39..bf863cfae63a50636c3fcc8fcf9761f1f117d218 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2746,6 +2746,8 @@ public class ServerGamePacketListenerImpl
@@ -2770,6 +2770,8 @@ public class ServerGamePacketListenerImpl
ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
@@ -5029,10 +5029,10 @@ index 6655d06e2011e20e7346dfe57527795269094d8a..6c14537c8376bd392524aefde8dfe76b
this.openTradingScreen(player, this.getDisplayName(), 1);
}
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 58932bc9256e9e2ff054ac007971c03187851b53..dda02ceae600f2909aad60bfef0ce29f83ca5a77 100644
index 3bc8b32e9eb39745c487d377474358c8c2e5b787..a65023d0929435785116682c1378428120340547 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -211,6 +211,19 @@ public abstract class Player extends LivingEntity {
@@ -218,6 +218,19 @@ public abstract class Player extends LivingEntity {
}
// CraftBukkit end

View File

@@ -0,0 +1,11 @@
--- a/io/papermc/paper/entity/activation/ActivationRange.java
+++ b/io/papermc/paper/entity/activation/ActivationRange.java
@@ -141,6 +_,8 @@
continue;
}
+ if (!player.level().purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur - AFK API
+
final int worldHeight = world.getHeight();
ActivationRange.maxBB = player.getBoundingBox().inflate(maxRange, worldHeight, maxRange);
ActivationType.MISC.boundingBox = player.getBoundingBox().inflate(miscActivationRange, worldHeight, miscActivationRange);

View File

@@ -35,11 +35,10 @@
@Override
public void displayClientMessage(Component chatComponent, boolean actionBar) {
this.sendSystemMessage(chatComponent, actionBar);
@@ -2234,6 +_,20 @@
this
@@ -2235,6 +_,20 @@
);
}
+
+ // Purpur start - Component related conveniences
+ public void sendMiniMessage(@Nullable String message) {
+ if (message != null && !message.isEmpty()) {
@@ -53,6 +52,76 @@
+ }
+ }
+ // Purpur end - Component related conveniences
+
public void sendSystemMessage(Component mesage) {
this.sendSystemMessage(mesage, false);
}
@@ -2373,7 +_,67 @@
public void resetLastActionTime() {
this.lastActionTime = Util.getMillis();
- }
+ this.setAfk(false); // Purpur - AFK API
+ }
+
+ // Purpur start - AFK API
+ 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, !org.bukkit.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, true);
+ }
+ }
+
+ ((ServerLevel) this.level()).updateSleepingPlayerList();
+ }
+
+ @Override
+ public boolean isAfk() {
+ return this.isAfk;
+ }
+
+ @Override
+ public boolean canBeCollidedWith() {
+ return !this.isAfk() && super.canBeCollidedWith();
+ }
+ // Purpur end - AFK API
public ServerStatsCounter getStats() {
return this.stats;

View File

@@ -0,0 +1,63 @@
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -326,6 +_,20 @@
this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat
}
+ // Purpur start - AFK API
+ 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 - AFK API
+
@Override
public void tick() {
if (this.ackBlockChangesUpTo > -1) {
@@ -384,6 +_,12 @@
if (this.player.getLastActionTime() > 0L
&& this.server.getPlayerIdleTimeout() > 0
&& Util.getMillis() - this.player.getLastActionTime() > this.server.getPlayerIdleTimeout() * 1000L * 60L && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits
+ // Purpur start - AFK API
+ 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 - AFK API
this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
this.disconnect(Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
}
@@ -629,6 +_,8 @@
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 - AFK API
+
Location oldTo = to.clone();
PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
this.cserver.getPluginManager().callEvent(event);
@@ -1460,7 +_,7 @@
movedWrongly = true;
if (event.getLogWarning())
// Paper end
- LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
+ LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur - AFK API
} // Paper
}
@@ -1525,6 +_,8 @@
this.lastPosZ = to.getZ();
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 - AFK API
Location oldTo = to.clone();
PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);

View File

@@ -0,0 +1,20 @@
--- a/net/minecraft/server/players/SleepStatus.java
+++ b/net/minecraft/server/players/SleepStatus.java
@@ -15,7 +_,7 @@
public boolean areEnoughDeepSleeping(int requiredSleepPercentage, List<ServerPlayer> sleepingPlayers) {
// CraftBukkit start
- int i = (int) sleepingPlayers.stream().filter(player -> player.isSleepingLongEnough() || player.fauxSleeping).count();
+ int i = (int) sleepingPlayers.stream().filter(player -> player.isSleepingLongEnough() || player.fauxSleeping || (player.level().purpurConfig.idleTimeoutCountAsSleeping && player.isAfk())).count(); // Purpur - AFK API
boolean anyDeepSleep = sleepingPlayers.stream().anyMatch(Player::isSleepingLongEnough);
return anyDeepSleep && i >= this.sleepersNeeded(requiredSleepPercentage);
// CraftBukkit end
@@ -43,7 +_,7 @@
for (ServerPlayer serverPlayer : players) {
if (!serverPlayer.isSpectator()) {
this.activePlayers++;
- if (serverPlayer.isSleeping() || serverPlayer.fauxSleeping) { // CraftBukkit
+ if (serverPlayer.isSleeping() || serverPlayer.fauxSleeping || (serverPlayer.level().purpurConfig.idleTimeoutCountAsSleeping && serverPlayer.isAfk())) { // CraftBukkit // Purpur - AFK API
this.sleepingPlayers++;
}
// CraftBukkit start

View File

@@ -0,0 +1,11 @@
--- a/net/minecraft/world/entity/EntitySelector.java
+++ b/net/minecraft/world/entity/EntitySelector.java
@@ -28,6 +_,8 @@
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 - Ability to control player's insomnia and phantoms
+ public static Predicate<Player> notAfk = (player) -> !player.isAfk(); // Purpur - AFK API
+
// Paper start - Affects Spawning API
public static final Predicate<Entity> PLAYER_AFFECTS_SPAWNING = (entity) -> {
return !entity.isSpectator() && entity.isAlive() && entity instanceof Player player && player.affectsSpawning;

View File

@@ -0,0 +1,13 @@
--- a/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
+++ b/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
@@ -64,6 +_,10 @@
return false;
} else if (this.selector != null && !this.selector.test(target, level)) {
return false;
+ // Purpur start - AFK API
+ } else if (!world.purpurConfig.idleTimeoutTargetPlayer && target instanceof net.minecraft.server.level.ServerPlayer player && player.isAfk()) {
+ return false;
+ // Purpur end - AFK API
} else {
if (entity == null) {
if (this.isCombat && (!target.canBeSeenAsEnemy() || level.getDifficulty() == Difficulty.PEACEFUL)) {

View File

@@ -0,0 +1,16 @@
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -205,6 +_,13 @@
public boolean fauxSleeping;
public int oldLevel = -1;
+ // Purpur start - AFK API
+ public abstract void setAfk(boolean afk);
+
+ public boolean isAfk() {
+ return false;
+ }
+ // Purpur end - AFK API
@Override
public org.bukkit.craftbukkit.entity.CraftHumanEntity getBukkitEntity() {
return (org.bukkit.craftbukkit.entity.CraftHumanEntity) super.getBukkitEntity();

View File

@@ -0,0 +1,11 @@
--- a/net/minecraft/world/level/EntityGetter.java
+++ b/net/minecraft/world/level/EntityGetter.java
@@ -185,7 +_,7 @@
default boolean hasNearbyAlivePlayer(double x, double y, double z, double distance) {
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)) { // Purpur - AFK API
double d = player.distanceToSqr(x, y, z);
if (distance < 0.0 || d < distance * distance) {
return true;

View File

@@ -1,6 +1,23 @@
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -3559,4 +_,10 @@
@@ -584,10 +_,15 @@
@Override
public void setPlayerListName(String name) {
+ // Purpur start - AFK API
+ setPlayerListName(name, false);
+ }
+ public void setPlayerListName(String name, boolean useMM) {
+ // Purpur end - AFK API
if (name == null) {
name = this.getName();
}
- this.getHandle().listName = name.equals(this.getName()) ? null : CraftChatMessage.fromStringOrNull(name);
+ this.getHandle().listName = name.equals(this.getName()) ? null : useMM ? io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(name)) : CraftChatMessage.fromStringOrNull(name); // Purpur - AFK API
if (this.getHandle().connection == null) return; // Paper - Updates are possible before the player has fully joined
for (ServerPlayer player : (List<ServerPlayer>) this.server.getHandle().players) {
if (player.getBukkitEntity().canSee(this)) {
@@ -3559,4 +_,26 @@
this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData()));
}
// Paper end - entity effect API
@@ -10,4 +27,20 @@
+ return getHandle().purpurClient;
+ }
+ // Purpur end - Purpur client support
+ // Purpur start - AFK API
+ @Override
+ public boolean isAfk() {
+ return getHandle().isAfk();
+ }
+
+ @Override
+ public void setAfk(boolean setAfk) {
+ getHandle().setAfk(setAfk);
+ }
+
+ @Override
+ public void resetIdleTimer() {
+ getHandle().resetLastActionTime();
+ }
+ // Purpur end - AFK API
}

View File

@@ -2,6 +2,7 @@ package org.purpurmc.purpur;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
@@ -157,8 +158,18 @@ public class PurpurConfig {
}
public static String cannotRideMob = "<red>You cannot mount that mob";
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() {
cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob);
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;

View File

@@ -71,6 +71,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 boolean babiesAreRidable = true;
public boolean untamedTamablesAreRidable = true;
public boolean useNightVisionWhenRiding = false;