Files
Purpur/patches/server/0039-Implement-AFK-API.patch
William Blake Galbreath ef94c821f3 Updated Upstream (Paper)
Upstream has released updates that appears to apply and compile correctly

Paper Changes:
23ee0a8a Fix issue with 0,0 chunks causing crash
bc8fafb2 Updated Upstream (CraftBukkit/Spigot)
ebbca5ce Drowned is a RangedEntity (fixes API for Drowned to support Ranged)
83b03eee Don't move existing players to world spawn
3b3e38fd Fix issue with loading chunk during ticking chunks issue
78431dca Update test server startup script
2020-04-10 01:43:21 -05:00

303 lines
14 KiB
Diff

From 5f4953b02d1597baf03c2a5df53b63bd69e595a6 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] Implement AFK API
---
.../java/net/minecraft/server/Entity.java | 1 +
.../net/minecraft/server/EntityHuman.java | 9 ++++
.../net/minecraft/server/EntityPlayer.java | 43 +++++++++++++++++++
.../net/minecraft/server/IEntityAccess.java | 34 ++++++---------
.../net/minecraft/server/IEntitySelector.java | 2 +
.../minecraft/server/PlayerConnection.java | 10 +++++
.../net/minecraft/server/WorldServer.java | 4 +-
.../net/pl3x/purpur/PurpurWorldConfig.java | 11 +++++
.../craftbukkit/entity/CraftPlayer.java | 17 ++++++++
.../java/org/spigotmc/ActivationRange.java | 1 +
src/main/resources/purpur.lang | 5 ++-
11 files changed, 112 insertions(+), 25 deletions(-)
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 313b74fb0f..cdb32e40ed 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -1385,6 +1385,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
return MathHelper.c(f * f + f1 * f1 + f2 * f2);
}
+ public double getDistanceSquared(double x, double y, double z) { return g(x, y, z); } // Purpur - OBFHELPER
public double g(double d0, double d1, double d2) {
double d3 = this.locX() - d0;
double d4 = this.locY() - d1;
diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java
index f372c13d8b..d69854b5c1 100644
--- a/src/main/java/net/minecraft/server/EntityHuman.java
+++ b/src/main/java/net/minecraft/server/EntityHuman.java
@@ -86,6 +86,15 @@ public abstract class EntityHuman extends EntityLiving {
}
// CraftBukkit end
+ // Purpur start
+ public void setAfk(boolean setAfk){
+ }
+
+ public boolean isAfk() {
+ return false;
+ }
+ // Purpur end
+
public EntityHuman(World world, GameProfile gameprofile) {
super(EntityTypes.PLAYER, world);
this.bV = ItemStack.a;
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index 24460ab531..d94ca2acf0 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
@@ -1623,8 +1623,51 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public void resetIdleTimer() {
this.cj = SystemUtils.getMonotonicMillis();
+ setAfk(false); // Purpur
}
+ // Purpur start
+ private boolean isAfk = false;
+
+ @Override
+ public void setAfk(boolean setAfk) {
+ if (this.isAfk == setAfk) {
+ return;
+ }
+
+ net.pl3x.purpur.event.PlayerAFKEvent event = new net.pl3x.purpur.event.PlayerAFKEvent(getBukkitEntity(), setAfk, world.purpurConfig.idleTimeoutKick, LocaleLanguage.getInstance().translateKey("idle.timeout.broadcast." + (setAfk ? "away" : "back")), !Bukkit.isPrimaryThread());
+ if (!event.callEvent() || event.shouldKick()) {
+ return;
+ }
+
+ this.isAfk = setAfk;
+
+ if (!setAfk) {
+ resetIdleTimer();
+ }
+
+ if (event.getBroadcastMsg() != null && !event.getBroadcastMsg().isEmpty()) {
+ server.getPlayerList().sendAll(new PacketPlayOutChat(new ChatMessage(event.getBroadcastMsg(), getScoreboardDisplayName())));
+ }
+
+ if (world.purpurConfig.idleTimeoutUpdateTabList) {
+ getBukkitEntity().setPlayerListName((setAfk ? "[AFK] " : "") + getName());
+ }
+
+ ((WorldServer) world).everyoneSleeping();
+ }
+
+ @Override
+ public boolean isAfk() {
+ return isAfk;
+ }
+
+ @Override
+ public boolean isCollidable(boolean ignoreClimbing) {
+ return !isAfk() && super.isCollidable(ignoreClimbing);
+ }
+ // Purpur end
+
public ServerStatisticManager getStatisticManager() {
return this.serverStatisticManager;
}
diff --git a/src/main/java/net/minecraft/server/IEntityAccess.java b/src/main/java/net/minecraft/server/IEntityAccess.java
index 774ec96f0a..e474ae08a1 100644
--- a/src/main/java/net/minecraft/server/IEntityAccess.java
+++ b/src/main/java/net/minecraft/server/IEntityAccess.java
@@ -116,28 +116,18 @@ public interface IEntityAccess {
return entityhuman;
}
- default boolean isPlayerNearby(double d0, double d1, double d2, double d3) {
- Iterator iterator = this.getPlayers().iterator();
-
- double d4;
-
- do {
- EntityHuman entityhuman;
-
- do {
- do {
- if (!iterator.hasNext()) {
- return false;
- }
-
- entityhuman = (EntityHuman) iterator.next();
- } while (!IEntitySelector.f.test(entityhuman));
- } while (!IEntitySelector.b.test(entityhuman));
-
- d4 = entityhuman.g(d0, d1, d2);
- } while (d3 >= 0.0D && d4 >= d3 * d3);
-
- return true;
+ // Purpur start
+ default boolean isPlayerNearby(double x, double y, double z, double distance) {
+ double distanceSq = distance * distance;
+ for (EntityHuman player : getPlayers()) {
+ if (IEntitySelector.notSpectator().test(player) && IEntitySelector.isLivingAlive().test(player) && IEntitySelector.notAfk.test(player)) {
+ if (distance < 0.0D || player.getDistanceSquared(x, y, z) < distanceSq) {
+ return true;
+ }
+ }
+ }
+ return false;
+ // Purpur end
}
@Nullable
diff --git a/src/main/java/net/minecraft/server/IEntitySelector.java b/src/main/java/net/minecraft/server/IEntitySelector.java
index e1fdee2794..4665671ef6 100644
--- a/src/main/java/net/minecraft/server/IEntitySelector.java
+++ b/src/main/java/net/minecraft/server/IEntitySelector.java
@@ -7,6 +7,7 @@ import javax.annotation.Nullable;
public final class IEntitySelector {
public static final Predicate<Entity> a = Entity::isAlive;
+ public static Predicate<EntityLiving> isLivingAlive() { return b; } // Purpur - OBFHELPER
public static final Predicate<EntityLiving> b = EntityLiving::isAlive;
public static final Predicate<Entity> c = (entity) -> {
return entity.isAlive() && !entity.isVehicle() && !entity.isPassenger();
@@ -24,6 +25,7 @@ public final class IEntitySelector {
return !entity.isSpectator();
};
public static Predicate<EntityHuman> isInsomniac = (player) -> MathHelper.clamp(((EntityPlayer) player).getStatisticManager().getStatisticValue(StatisticList.CUSTOM.b(StatisticList.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= 72000; // Purpur
+ public static Predicate<EntityHuman> notAfk = (player) -> !player.isAfk(); // Purpur
public static Predicate<Entity> a(double d0, double d1, double d2, double d3) {
double d4 = d3 * d3;
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
index 895e34ed34..f92516069a 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -223,6 +223,12 @@ public class PlayerConnection implements PacketListenerPlayIn {
}
if (this.player.F() > 0L && this.minecraftServer.getIdleTimeout() > 0 && SystemUtils.getMonotonicMillis() - this.player.F() > (long) (this.minecraftServer.getIdleTimeout() * 1000 * 60)) {
+ // Purpur start
+ this.player.setAfk(true);
+ if (!this.player.world.purpurConfig.idleTimeoutKick) {
+ return;
+ }
+ // Purpur end
this.player.resetIdleTimer(); // CraftBukkit - SPIGOT-854
this.disconnect(new ChatMessage("multiplayer.disconnect.idling", new Object[0]));
}
@@ -439,6 +445,8 @@ public class PlayerConnection implements PacketListenerPlayIn {
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()) this.player.resetIdleTimer(); // Purpur
+
// Skip the first time we do this
if (true) { // Spigot - don't skip any move events
Location oldTo = to.clone();
@@ -1108,6 +1116,8 @@ public class PlayerConnection implements PacketListenerPlayIn {
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()) this.player.resetIdleTimer(); // 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/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index 42798f6e00..efe9796c4a 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -383,7 +383,7 @@ public class WorldServer extends World {
}
if (this.everyoneSleeping && this.players.stream().noneMatch((entityplayer) -> {
- return !entityplayer.isSpectator() && !entityplayer.isDeeplySleeping() && !entityplayer.fauxSleeping; // CraftBukkit
+ return !entityplayer.isSpectator() && !entityplayer.isDeeplySleeping() && !entityplayer.fauxSleeping && !(purpurConfig.idleTimeoutCountAsSleeping && entityplayer.isAfk()); // CraftBukkit // Purpur
})) {
// CraftBukkit start
long l = this.worldData.getDayTime() + 24000L;
@@ -696,7 +696,7 @@ public class WorldServer extends World {
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
- if (entityplayer.isSpectator() || (entityplayer.fauxSleeping && !entityplayer.isSleeping())) { // CraftBukkit
+ if (entityplayer.isSpectator() || (entityplayer.fauxSleeping && !entityplayer.isSleeping()) || (purpurConfig.idleTimeoutCountAsSleeping && entityplayer.isAfk())) { // CraftBukkit // Purpur
++i;
} else if (entityplayer.isSleeping()) {
++j;
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index c6bf9395c1..76f8c8fed1 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -138,6 +138,17 @@ public class PurpurWorldConfig {
disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath);
}
+ public boolean idleTimeoutKick = true;
+ public boolean idleTimeoutTickNearbyEntities = true;
+ public boolean idleTimeoutCountAsSleeping = false;
+ public boolean idleTimeoutUpdateTabList = false;
+ private void idleTimeoutSettings() {
+ idleTimeoutKick = getBoolean("gameplay-mechanics.player.idle-timeout.kick-if-idle", idleTimeoutKick);
+ 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);
+ }
+
public boolean batRidable = false;
public boolean batRidableInWater = false;
public boolean batRequireShiftToMount = true;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 33cac5fcb1..aa6f67cad3 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2057,4 +2057,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
return spigot;
}
// Spigot end
+
+ // Purpur start
+ @Override
+ public boolean isAfk() {
+ return getHandle().isAfk();
+ }
+
+ @Override
+ public void setAfk(boolean setAfk) {
+ getHandle().setAfk(setAfk);
+ }
+
+ @Override
+ public void resetIdleTimer() {
+ getHandle().resetIdleTimer();
+ }
+ // Purpur end
}
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index 795817177a..17c4d7ec9b 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -146,6 +146,7 @@ public class ActivationRange
{
player.activatedTick = MinecraftServer.currentTick;
+ if (!player.world.purpurConfig.idleTimeoutTickNearbyEntities && player.isAfk()) continue; // Purpur
maxBB = player.getBoundingBox().grow( maxRange, 256, maxRange );
ActivationType.MISC.boundingBox = player.getBoundingBox().grow( miscActivationRange, 256, miscActivationRange );
ActivationType.RAIDER.boundingBox = player.getBoundingBox().grow( raiderActivationRange, 256, raiderActivationRange );
diff --git a/src/main/resources/purpur.lang b/src/main/resources/purpur.lang
index 0967ef424b..f4694dbcce 100644
--- a/src/main/resources/purpur.lang
+++ b/src/main/resources/purpur.lang
@@ -1 +1,4 @@
-{}
+{
+ "idle.timeout.broadcast.away": "§e§o%s is now AFK",
+ "idle.timeout.broadcast.back": "§e§o%s is no longer AFK"
+}
--
2.24.0