mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-17 16:37:43 +01:00
AFK API
This commit is contained in:
committed by
granny
parent
c5902528c1
commit
fb0d9a1cd6
@@ -1,113 +0,0 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: William Blake Galbreath <blake.galbreath@gmail.com>
|
|
||||||
Date: Sat, 10 Aug 2019 22:19:56 -0500
|
|
||||||
Subject: [PATCH] AFK API
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
|
|
||||||
index c8365c38c91b3e6c4f721074f0646fe5adffbdf6..ed0f892ed987419809fe1a0390b6278c99659919 100644
|
|
||||||
--- a/src/main/java/org/bukkit/entity/Player.java
|
|
||||||
+++ b/src/main/java/org/bukkit/entity/Player.java
|
|
||||||
@@ -3919,5 +3919,25 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
|
|
||||||
* @return True if Player uses Purpur Client
|
|
||||||
*/
|
|
||||||
public boolean usesPurpurClient();
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * Check if player is AFK
|
|
||||||
+ *
|
|
||||||
+ * @return True if AFK
|
|
||||||
+ */
|
|
||||||
+ boolean isAfk();
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * Set player as AFK
|
|
||||||
+ *
|
|
||||||
+ * @param setAfk Whether to set AFK or not
|
|
||||||
+ */
|
|
||||||
+ void setAfk(boolean setAfk);
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * Reset the idle timer back to 0
|
|
||||||
+ * @deprecated Use {@link #resetIdleDuration()} instead
|
|
||||||
+ */
|
|
||||||
+ void resetIdleTimer();
|
|
||||||
// Purpur end
|
|
||||||
}
|
|
||||||
diff --git a/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java b/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000000000000000000000000000000000000..e9637b82014fe3f4f4671b24d18f77f3d5e4b8ad
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/src/main/java/org/purpurmc/purpur/event/PlayerAFKEvent.java
|
|
||||||
@@ -0,0 +1,71 @@
|
|
||||||
+package org.purpurmc.purpur.event;
|
|
||||||
+
|
|
||||||
+import org.bukkit.entity.Player;
|
|
||||||
+import org.bukkit.event.Cancellable;
|
|
||||||
+import org.bukkit.event.HandlerList;
|
|
||||||
+import org.bukkit.event.player.PlayerEvent;
|
|
||||||
+import org.jetbrains.annotations.ApiStatus;
|
|
||||||
+import org.jspecify.annotations.NullMarked;
|
|
||||||
+import org.jspecify.annotations.Nullable;
|
|
||||||
+
|
|
||||||
+@NullMarked
|
|
||||||
+public class PlayerAFKEvent extends PlayerEvent implements Cancellable {
|
|
||||||
+ private static final HandlerList handlers = new HandlerList();
|
|
||||||
+ private final boolean setAfk;
|
|
||||||
+ private boolean shouldKick;
|
|
||||||
+ private @Nullable String broadcast;
|
|
||||||
+ private boolean cancel;
|
|
||||||
+
|
|
||||||
+ @ApiStatus.Internal
|
|
||||||
+ public PlayerAFKEvent(Player player, boolean setAfk, boolean shouldKick, @Nullable String broadcast, boolean async) {
|
|
||||||
+ super(player, async);
|
|
||||||
+ this.setAfk = setAfk;
|
|
||||||
+ this.shouldKick = shouldKick;
|
|
||||||
+ this.broadcast = broadcast;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /**
|
|
||||||
+ * Whether player is going afk or coming back
|
|
||||||
+ *
|
|
||||||
+ * @return True if going afk. False is coming back
|
|
||||||
+ */
|
|
||||||
+ public boolean isGoingAfk() {
|
|
||||||
+ return setAfk;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public boolean shouldKick() {
|
|
||||||
+ return shouldKick;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void setShouldKick(boolean shouldKick) {
|
|
||||||
+ this.shouldKick = shouldKick;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Nullable
|
|
||||||
+ public String getBroadcastMsg() {
|
|
||||||
+ return broadcast;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public void setBroadcastMsg(@Nullable String broadcast) {
|
|
||||||
+ this.broadcast = broadcast;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public boolean isCancelled() {
|
|
||||||
+ return cancel;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public void setCancelled(boolean cancel) {
|
|
||||||
+ this.cancel = cancel;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public HandlerList getHandlers() {
|
|
||||||
+ return handlers;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ public static HandlerList getHandlerList() {
|
|
||||||
+ return handlers;
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
@@ -1,331 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
|
||||||
index 8207208d6fb3f982e9909add9e74a0dda69e8120..5e8a5e8e4120420a170c4733ae217e7948cef46c 100644
|
|
||||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
|
||||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
|
||||||
@@ -2621,8 +2621,68 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
|
|
||||||
|
|
||||||
public void resetLastActionTime() {
|
|
||||||
this.lastActionTime = Util.getMillis();
|
|
||||||
+ this.setAfk(false); // Purpur
|
|
||||||
}
|
|
||||||
|
|
||||||
+ // 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, !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;
|
|
||||||
}
|
|
||||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
||||||
index fbc59503316d566e88b037851afd74e5469c281b..830e0705f3a3d45fcf0eab6b8eea403c3023f3f9 100644
|
|
||||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
||||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
||||||
@@ -344,6 +344,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
||||||
private boolean justTeleported = false;
|
|
||||||
// CraftBukkit end
|
|
||||||
|
|
||||||
+ // 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) {
|
|
||||||
@@ -400,6 +414,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
||||||
this.recipeSpamPackets.tick(); // Paper - auto recipe limit
|
|
||||||
this.dropSpamThrottler.tick();
|
|
||||||
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 - 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) Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause
|
|
||||||
}
|
|
||||||
@@ -656,6 +676,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
||||||
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);
|
|
||||||
@@ -1554,7 +1576,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
||||||
movedWrongly = true;
|
|
||||||
if (event.getLogWarning())
|
|
||||||
// Paper end
|
|
||||||
- ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
|
|
||||||
+ ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!, ({})", this.player.getName().getString(), d11); // Purpur - AFK API
|
|
||||||
} // Paper
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1622,6 +1644,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
|
||||||
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);
|
|
||||||
diff --git a/net/minecraft/server/players/SleepStatus.java b/net/minecraft/server/players/SleepStatus.java
|
|
||||||
index 823efad652d8ff9e96b99375b102fef6f017716e..bdf0240840d92bf95f94c6fb1125eeaa105e303b 100644
|
|
||||||
--- a/net/minecraft/server/players/SleepStatus.java
|
|
||||||
+++ b/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 - AFK API
|
|
||||||
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 - AFK API
|
|
||||||
++this.sleepingPlayers;
|
|
||||||
}
|
|
||||||
// CraftBukkit start
|
|
||||||
diff --git a/net/minecraft/world/entity/EntitySelector.java b/net/minecraft/world/entity/EntitySelector.java
|
|
||||||
index 6bf691fcc6486bde73bae30eff09142802c29eda..d99b223be90f0c04bb9274228ad323a7c7f218b2 100644
|
|
||||||
--- a/net/minecraft/world/entity/EntitySelector.java
|
|
||||||
+++ b/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 - Ability to control player's insomnia and phantoms
|
|
||||||
+ public static Predicate<Player> notAfk = (player) -> !player.isAfk(); // Purpur - AFK API
|
|
||||||
|
|
||||||
private EntitySelector() {}
|
|
||||||
// Paper start - Affects Spawning API
|
|
||||||
diff --git a/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
||||||
index 52982c1e6a4da36392569c791853279f5f9ac31a..ebb827e213a3ba5eeb2fe5b78f5dee99403097b6 100644
|
|
||||||
--- a/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
|
|
||||||
+++ b/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(target, world)) {
|
|
||||||
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 (tester == null) {
|
|
||||||
if (this.isCombat && (!target.canBeSeenAsEnemy() || world.getDifficulty() == Difficulty.PEACEFUL)) {
|
|
||||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
|
||||||
index 87c6378104ff47549c751e09afb6cfcd9b8dcf5d..2f69a511db8d43fbd3a17387cded1d3573579fce 100644
|
|
||||||
--- a/net/minecraft/world/entity/player/Player.java
|
|
||||||
+++ b/net/minecraft/world/entity/player/Player.java
|
|
||||||
@@ -206,6 +206,13 @@ public abstract class Player extends LivingEntity {
|
|
||||||
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 CraftHumanEntity getBukkitEntity() {
|
|
||||||
return (CraftHumanEntity) super.getBukkitEntity();
|
|
||||||
diff --git a/net/minecraft/world/level/EntityGetter.java b/net/minecraft/world/level/EntityGetter.java
|
|
||||||
index 5d7a6e4b73f032db356e7ec369b150013e940ee6..e164833de0c29eed9025dd4af3f2bb74c92d2250 100644
|
|
||||||
--- a/net/minecraft/world/level/EntityGetter.java
|
|
||||||
+++ b/net/minecraft/world/level/EntityGetter.java
|
|
||||||
@@ -184,7 +184,7 @@ public interface EntityGetter extends ca.spottedleaf.moonrise.patches.chunk_syst
|
|
||||||
|
|
||||||
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)) { // Purpur - AFK API
|
|
||||||
double d = player.distanceToSqr(x, y, z);
|
|
||||||
if (range < 0.0 || 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 c5bd2a45b32e8dff83c148379544db125684622a..16671636da1b6da62a0bdf0daab745fcd89143b7 100644
|
|
||||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
||||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
|
||||||
@@ -584,10 +584,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
||||||
|
|
||||||
@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)) {
|
|
||||||
@@ -3572,4 +3577,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
||||||
index 1321955eb23272d96e3028c1c46e75f6c1eaade0..82f0ae89ef3f0dc614edb4ccd3caa66cb387044c 100644
|
|
||||||
--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
||||||
+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java
|
|
||||||
@@ -177,8 +177,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;
|
|
||||||
diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
||||||
index ee9bcb7d011f20575cbbbe2e0ded1e53087aba7a..9b1a4502aa6c26c7524ec17697250317b7f381fd 100644
|
|
||||||
--- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
||||||
+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java
|
|
||||||
@@ -91,6 +91,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;
|
|
||||||
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
|
|
||||||
index 1d438ef44cbe4d1eedfba36d8fe5d2ad53464921..24d7eca3f0b06602a1026eda3432f0a4255d8b01 100644
|
|
||||||
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
|
||||||
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
|
||||||
@@ -199,6 +199,8 @@ 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 );
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
--- a/src/main/java/org/bukkit/entity/Player.java
|
--- a/src/main/java/org/bukkit/entity/Player.java
|
||||||
+++ b/src/main/java/org/bukkit/entity/Player.java
|
+++ b/src/main/java/org/bukkit/entity/Player.java
|
||||||
@@ -3911,4 +_,13 @@
|
@@ -3911,4 +_,33 @@
|
||||||
*/
|
*/
|
||||||
void sendEntityEffect(org.bukkit.@NotNull EntityEffect effect, @NotNull Entity target);
|
void sendEntityEffect(org.bukkit.@NotNull EntityEffect effect, @NotNull Entity target);
|
||||||
// Paper end - entity effect API
|
// Paper end - entity effect API
|
||||||
@@ -12,5 +12,25 @@
|
|||||||
+ * @return true if player uses PurpurClient
|
+ * @return true if player uses PurpurClient
|
||||||
+ */
|
+ */
|
||||||
+ public boolean usesPurpurClient();
|
+ public boolean usesPurpurClient();
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Check if player is AFK
|
||||||
|
+ *
|
||||||
|
+ * @return True if AFK
|
||||||
|
+ */
|
||||||
|
+ boolean isAfk();
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Set player as AFK
|
||||||
|
+ *
|
||||||
|
+ * @param setAfk Whether to set AFK or not
|
||||||
|
+ */
|
||||||
|
+ void setAfk(boolean setAfk);
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Reset the idle timer back to 0
|
||||||
|
+ * @deprecated Use {@link #resetIdleDuration()} instead
|
||||||
|
+ */
|
||||||
|
+ void resetIdleTimer();
|
||||||
+ // Purpur end
|
+ // Purpur end
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package org.purpurmc.purpur.event;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Cancellable;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.player.PlayerEvent;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
public class PlayerAFKEvent extends PlayerEvent implements Cancellable {
|
||||||
|
private static final HandlerList handlers = new HandlerList();
|
||||||
|
private final boolean setAfk;
|
||||||
|
private boolean shouldKick;
|
||||||
|
private @Nullable String broadcast;
|
||||||
|
private boolean cancel;
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public PlayerAFKEvent(Player player, boolean setAfk, boolean shouldKick, @Nullable String broadcast, boolean async) {
|
||||||
|
super(player, async);
|
||||||
|
this.setAfk = setAfk;
|
||||||
|
this.shouldKick = shouldKick;
|
||||||
|
this.broadcast = broadcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether player is going afk or coming back
|
||||||
|
*
|
||||||
|
* @return True if going afk. False is coming back
|
||||||
|
*/
|
||||||
|
public boolean isGoingAfk() {
|
||||||
|
return setAfk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldKick() {
|
||||||
|
return shouldKick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShouldKick(boolean shouldKick) {
|
||||||
|
this.shouldKick = shouldKick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getBroadcastMsg() {
|
||||||
|
return broadcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBroadcastMsg(@Nullable String broadcast) {
|
||||||
|
this.broadcast = broadcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCancelled(boolean cancel) {
|
||||||
|
this.cancel = cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandlerList getHandlers() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HandlerList getHandlerList() {
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ index ebeeb63c3dca505a3ce8b88feaa5d2ca20ec24a2..0029717fbd4f2475b07abf4f7036cebb
|
|||||||
public LevelChunk getChunkIfLoaded(int x, int z) {
|
public LevelChunk getChunkIfLoaded(int x, int z) {
|
||||||
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
|
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
|
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
|
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||||
+++ b/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
|
@@ -838,6 +838,15 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||||
@@ -62,10 +62,10 @@ index dc146170def1b349291916f13164a5f4c43ee6f2..544b87f020ad2177b6ec86ce2d1e49a1
|
|||||||
|
|
||||||
private void updatePlayerAttributes() {
|
private void updatePlayerAttributes() {
|
||||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
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
|
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||||
+++ b/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);
|
ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event);
|
||||||
|
|
||||||
@@ -5029,10 +5029,10 @@ index 6655d06e2011e20e7346dfe57527795269094d8a..6c14537c8376bd392524aefde8dfe76b
|
|||||||
this.openTradingScreen(player, this.getDisplayName(), 1);
|
this.openTradingScreen(player, this.getDisplayName(), 1);
|
||||||
}
|
}
|
||||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
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
|
--- a/net/minecraft/world/entity/player/Player.java
|
||||||
+++ b/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
|
// CraftBukkit end
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -35,11 +35,10 @@
|
|||||||
@Override
|
@Override
|
||||||
public void displayClientMessage(Component chatComponent, boolean actionBar) {
|
public void displayClientMessage(Component chatComponent, boolean actionBar) {
|
||||||
this.sendSystemMessage(chatComponent, actionBar);
|
this.sendSystemMessage(chatComponent, actionBar);
|
||||||
@@ -2234,6 +_,20 @@
|
@@ -2235,6 +_,20 @@
|
||||||
this
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+
|
|
||||||
+ // Purpur start - Component related conveniences
|
+ // Purpur start - Component related conveniences
|
||||||
+ public void sendMiniMessage(@Nullable String message) {
|
+ public void sendMiniMessage(@Nullable String message) {
|
||||||
+ if (message != null && !message.isEmpty()) {
|
+ if (message != null && !message.isEmpty()) {
|
||||||
@@ -53,6 +52,76 @@
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ // Purpur end - Component related conveniences
|
+ // Purpur end - Component related conveniences
|
||||||
|
+
|
||||||
public void sendSystemMessage(Component mesage) {
|
public void sendSystemMessage(Component mesage) {
|
||||||
this.sendSystemMessage(mesage, false);
|
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;
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
@@ -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)) {
|
||||||
@@ -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();
|
||||||
@@ -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;
|
||||||
@@ -1,6 +1,23 @@
|
|||||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||||
+++ b/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()));
|
this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData()));
|
||||||
}
|
}
|
||||||
// Paper end - entity effect API
|
// Paper end - entity effect API
|
||||||
@@ -10,4 +27,20 @@
|
|||||||
+ return getHandle().purpurClient;
|
+ return getHandle().purpurClient;
|
||||||
+ }
|
+ }
|
||||||
+ // Purpur end - Purpur client support
|
+ // 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.purpurmc.purpur;
|
|||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
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 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() {
|
private static void messages() {
|
||||||
cannotRideMob = getString("settings.messages.cannot-ride-mob", cannotRideMob);
|
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;
|
public static int barrelRows = 3;
|
||||||
|
|||||||
@@ -71,6 +71,24 @@ public class PurpurWorldConfig {
|
|||||||
return value.isEmpty() ? fallback : value;
|
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 babiesAreRidable = true;
|
||||||
public boolean untamedTamablesAreRidable = true;
|
public boolean untamedTamablesAreRidable = true;
|
||||||
public boolean useNightVisionWhenRiding = false;
|
public boolean useNightVisionWhenRiding = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user