Compare commits

..

4 Commits

Author SHA1 Message Date
Helvetica Volubi cfc73efc9a Update Luminol 2026-06-14 18:43:18 +08:00
Helvetica Volubi 0989bcaec7 [ci skip]refactor: convert AbstractConcurrentTable to interface and update ConcurrentTable implementation 2026-06-13 01:19:56 +08:00
Helvetica Volubi 3763d0b885 Update clip & weight 2026-06-10 03:20:21 +08:00
Roberta001 cd3db4ae8c Fix bot connection region crash (#149)
* Fix: bot connection causing region crash, fix readme

* Fix NPE on Region Shutdown with bots

* simple fix up

* Revert "Fix NPE on Region Shutdown with bots"

This reverts commit b55c982cb9.

* Revert "fix: fix a bug in server shutdown with fakeplayer"

This reverts commit a721c27e96.

* fix: improve bot shutdown handling to prevent region crashes

---------

Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun>
2026-06-09 16:17:13 +08:00
16 changed files with 121 additions and 104 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ git clone https://github.com/LuminolMC/Lophine.git
cd Lophine
# 应用补丁并构建 Paperclip JAR
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
./gradlew applyAllPatches && ./gradlew createPaperclipJar
```
构建完成后,您可以在 `lophine-server/build/libs` 目录中找到生成的 JAR 文件。
+1 -1
View File
@@ -45,7 +45,7 @@ git clone https://github.com/LuminolMC/Lophine.git
cd Lophine
# Apply patches and build Paperclip JAR
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
./gradlew applyAllPatches && ./gradlew createPaperclipJar
```
After building, you can find the generated JAR file in the `lophine-server/build/libs` directory.
+3 -3
View File
@@ -2,14 +2,14 @@ group=fun.bm.lophine
mcVersion=26.1.2
apiVersion=26.1.2
channel=STABLE
clipVersion=3.0.17
weightVersion=2.0.12
clipVersion=3.0.18
weightVersion=2.0.15
# true for release, false for skip release, pre for pre-release
release=true
# true for push to repo, false for skip push repo, auto for detect by release value
pushRepo=auto
luminolRef=80b2a6c27bd06f252c7c797867f8d8cb77391063
luminolRef=b672b2fbed8d0f1f7de84dfcf373482dd2e5ee51
org.gradle.configuration-cache=true
org.gradle.caching=true
@@ -26,10 +26,10 @@ index 49c7f8194be8222e329fc3215a6aa6db0b221b11..c6c968d28e0d14a4c50e66bdd5176a00
loadFromJson(stream, output);
} catch (JsonParseException | IOException var7) {
diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java
index 5b46b224bb0fea3a7e0f721ce9770657ffe0c71d..44d594da98615a8bbfb2560e0a63d2e7aab3cc7e 100644
index 143ad4e342b184aa02b6fbbe81508e244eebc7a2..89be42b8519522398920eb424c894fd2ab8c9666 100644
--- a/net/minecraft/server/Main.java
+++ b/net/minecraft/server/Main.java
@@ -154,6 +154,8 @@ public class Main {
@@ -155,6 +155,8 @@ public class Main {
return;
}
@@ -33,7 +33,7 @@ index efbbc1a40d203a2818eeda2a4e938b9a6117f1c3..6b85a5ad9af0c57a0d4d5b55f62b82c5
private boolean encrypted;
private final java.util.concurrent.atomic.AtomicBoolean disconnectionHandled = new java.util.concurrent.atomic.AtomicBoolean(false); // Folia - region threading - may be called concurrently during configuration stage
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 9e290fa626caffac148920c54479a75ad0919e70..54eaf4f7f8cf728d5d02d88de8d57d88504bc46d 100644
index 9e290fa626caffac148920c54479a75ad0919e70..75d93d7d8bc6077dd80e499cd43327a4afe58f51 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -297,6 +297,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -45,15 +45,24 @@ index 9e290fa626caffac148920c54479a75ad0919e70..54eaf4f7f8cf728d5d02d88de8d57d88
public static <S extends MinecraftServer> S spin(final Function<Thread, S> factory) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
AtomicReference<S> serverReference = new AtomicReference<>();
@@ -1120,6 +1122,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
LOGGER.info("Stopping server");
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
+ this.getBotList().removeAll(); // Leaves - save or remove bot
// CraftBukkit start
if (this.server != null) {
if (false) this.server.spark.disable(); // Paper - spark // Luminol - Force disable builtin spark
@@ -1695,7 +1698,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1226,6 +1228,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.safeShutdown(wait, false);
}
public void safeShutdown(final boolean wait, final boolean isRestarting) {
+ // Lophine start - Folia changes
+ safeShutdown(wait, isRestarting, false);
+ }
+
+ public void safeShutdown(final boolean wait, final boolean isRestarting, boolean force) {
+ if (!force && !this.getBotList().forceShutdown && !this.getBotList().removeAll()) { // Leaves - save or remove bot
+ this.getPlayerList().broadcastSystemMessage(Component.literal("Bot Still need to save, please wait! If you want to shuntdown without bot data saving, please use /stop force"), false);
+ return;
+ }
+ // Lophine end - Folia changes
this.isRestarting = isRestarting;
this.hasLoggedStop = true; // Paper - Debugging
if (this.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging
@@ -1695,7 +1707,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
int emptyTickThreshold = this.pauseWhenEmptySeconds() * 20;
this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping
if (false && emptyTickThreshold > 0) { // Folia - region threading - this is complicated to implement, and even if done correctly is messy
@@ -62,7 +71,7 @@ index 9e290fa626caffac148920c54479a75ad0919e70..54eaf4f7f8cf728d5d02d88de8d57d88
this.emptyTicks++;
} else {
this.emptyTicks = 0;
@@ -1827,6 +1830,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1827,6 +1839,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.isSaving = true;
if (playerSaveInterval > 0) {
this.playerList.saveAll(playerSaveInterval);
@@ -70,7 +79,7 @@ index 9e290fa626caffac148920c54479a75ad0919e70..54eaf4f7f8cf728d5d02d88de8d57d88
}
if (region == null && fullSave) { // Folia - region threading - don't auto save level data
this.saveGlobalData(false);
@@ -2065,6 +2069,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2065,6 +2078,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected void tickConnection() {
this.getConnection().tick();
@@ -78,7 +87,7 @@ index 9e290fa626caffac148920c54479a75ad0919e70..54eaf4f7f8cf728d5d02d88de8d57d88
}
public void forceGameTimeSynchronization() {
@@ -3270,6 +3275,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -3270,6 +3284,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.debugSubscribers;
}
@@ -213,6 +222,22 @@ index 636f56bfc0e9075b257e2eb14e858975bdedd801..20cbc0e65fea3fc2d0033259c2f35e15
if (!list.isOp(player)) {
list.op(player);
count++;
diff --git a/net/minecraft/server/commands/StopCommand.java b/net/minecraft/server/commands/StopCommand.java
index e9c9a468f046832e8eaa999bb57da261b4633604..2efe24c0378a80dd2750b10a54c1887816236c1a 100644
--- a/net/minecraft/server/commands/StopCommand.java
+++ b/net/minecraft/server/commands/StopCommand.java
@@ -11,6 +11,10 @@ public class StopCommand {
c.getSource().sendSuccess(() -> Component.translatable("commands.stop.stopping"), true);
c.getSource().getServer().halt(false);
return 1;
- }));
+ }).then(Commands.literal("force").requires(Commands.hasPermission(Commands.LEVEL_OWNERS)).executes(c -> {
+ c.getSource().sendSuccess(() -> Component.translatable("commands.stop.stopping"), true);
+ c.getSource().getServer().safeShutdown(false, false, true);
+ return 1;
+ })));
}
}
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 1bc1e9a77dca7acfe5f989f6439f185d0d2e20a6..1790e8c141a8d3f7ccf4b11f9a28b3582325e317 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
@@ -84,10 +84,10 @@ index 7e3912b8b29e40eb3347f1451b0fd3ebee2af4f1..0834e402e6af7028e08851378e15390e
result.add(player);
if (result.size() >= limit) {
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 54eaf4f7f8cf728d5d02d88de8d57d88504bc46d..269b884ff30d908f772d1c0cf05076e359bcdd7e 100644
index 75d93d7d8bc6077dd80e499cd43327a4afe58f51..3b9a4f4b5794faa4955018858c94fd5c2d6ada3d 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1921,7 +1921,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1930,7 +1930,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
private ServerStatus.Players buildPlayerStatus() {
@@ -96,7 +96,7 @@ index 54eaf4f7f8cf728d5d02d88de8d57d88504bc46d..269b884ff30d908f772d1c0cf05076e3
int maxPlayers = this.getMaxPlayers();
if (this.hidesOnlinePlayers()) {
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
@@ -2142,7 +2142,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2151,7 +2151,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public int getPlayerCount() {
@@ -105,7 +105,7 @@ index 54eaf4f7f8cf728d5d02d88de8d57d88504bc46d..269b884ff30d908f772d1c0cf05076e3
}
public String[] getPlayerNames() {
@@ -2354,7 +2354,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2363,7 +2363,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
} else {
int count = 0;
@@ -114,7 +114,7 @@ index 54eaf4f7f8cf728d5d02d88de8d57d88504bc46d..269b884ff30d908f772d1c0cf05076e3
player.getBukkitEntity().taskScheduler.schedule((ServerPlayer updatedPlayer) -> { // Folia - region threading
// Paper start - Expand PlayerGameModeChangeEvent
org.bukkit.event.player.PlayerGameModeChangeEvent event = updatedPlayer.setGameMode(gameType, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null); // Folia - region threading
@@ -2541,7 +2541,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2550,7 +2550,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (Thread.currentThread() != this.serverThread) return; // Paper
// Paper start - we don't need to save everything, just advancements
// this.getPlayerList().saveAll();
@@ -123,7 +123,7 @@ index 54eaf4f7f8cf728d5d02d88de8d57d88504bc46d..269b884ff30d908f772d1c0cf05076e3
player.getAdvancements().save();
}
// Paper end - we don't need to save everything, just advancements
@@ -2677,7 +2677,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2686,7 +2686,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
PlayerList playerList = this.getPlayerList();
UserWhiteList whiteList = playerList.getWhiteList();
@@ -32,10 +32,10 @@ index 24f5b890b0126fc817863f99b676da1a548f7608..78bea975c15130c91c90f48c868ac32e
throw PacketUtils.makeReportedException(var3, this.packet, this.listener);
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 269b884ff30d908f772d1c0cf05076e359bcdd7e..fe5f6eb98aaa29b2745f4db51b9daad2666ec8f1 100644
index 3b9a4f4b5794faa4955018858c94fd5c2d6ada3d..19bef18e606d873308d08987d90e298703efb574 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2010,11 +2010,26 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2019,11 +2019,26 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
foliaProfiler.startTimer(level.tickTimerId); try { // Folia - profiler
level.tick(haveTime, region); // Folia - region threading
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/3e7f4a313ff0d2469
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 63176a938619615646f864054380752d400e1ad4..6c97fe9934e53160ed5631e37499544a614afbdc 100644
index ba0779211405b8b83f7c7e7b1c55b3e4846029a3..5f685b07878443bfe5f63147a0728df0708d79ad 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -483,6 +483,7 @@ public final class CraftServer implements Server {
@@ -53,7 +53,7 @@ index 5339ff469a38d35e72ea7227503a84395462f84a..d75b4ecb24f2ea42afc82ac255775d8f
if (nmsEntity.level() != this.getHandle().getLevel()) {
throw new IllegalArgumentException(entity + " wasn't created with this world, you must create the entity with the world you want to add it to.");
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 6c97fe9934e53160ed5631e37499544a614afbdc..60362385861f7bac2773692344aea2986dec2f6d 100644
index 5f685b07878443bfe5f63147a0728df0708d79ad..0eb89fc127738e02e8d4d49418b83baaffe00cbd 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -303,6 +303,7 @@ public final class CraftServer implements Server {
@@ -81,7 +81,7 @@ index 6c97fe9934e53160ed5631e37499544a614afbdc..60362385861f7bac2773692344aea298
return false;
}
@@ -3106,6 +3108,13 @@ public final class CraftServer implements Server {
@@ -3113,6 +3115,13 @@ public final class CraftServer implements Server {
}
// Leaves end - Feature API
@@ -21,7 +21,7 @@ index e1f456b31ffda9370b63e51f343847d6f9f62263..8a0f85241cc1530aacccc8b6ea24b9de
}
// Leaves end - skip bot
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 60362385861f7bac2773692344aea2986dec2f6d..0aacbb75751addf46b40f2dc916b7282a635ed11 100644
index 0eb89fc127738e02e8d4d49418b83baaffe00cbd..53e49df883ec0fab984f2329d044d12ed6b1edff 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -304,6 +304,7 @@ public final class CraftServer implements Server {
@@ -41,7 +41,7 @@ index 60362385861f7bac2773692344aea2986dec2f6d..0aacbb75751addf46b40f2dc916b7282
@Override
public CraftPlayer apply(ServerPlayer player) {
return player.getBukkitEntity();
@@ -3179,4 +3180,11 @@ public final class CraftServer implements Server {
@@ -3186,4 +3187,11 @@ public final class CraftServer implements Server {
}
}
// Folia end - region TPS API
@@ -3,34 +3,34 @@ package fun.bm.lophine.utils.concurrent;
import java.util.List;
import java.util.Map;
public abstract class AbstractConcurrentTable<X, Y, Z> {
public abstract void put(X x, Y y, Z z);
public interface AbstractConcurrentTable<X, Y, Z> {
void put(X x, Y y, Z z);
public abstract void remove(X x, Y y, Z z);
void remove(X x, Y y, Z z);
public abstract List<Z> getZ(X x, Y y);
List<Z> getZ(X x, Y y);
public abstract List<Y> getY(X x, Z z);
List<Y> getY(X x, Z z);
public abstract List<X> getX(Y y, Z z);
List<X> getX(Y y, Z z);
public abstract Map<X, Y> getXY(Z z);
Map<X, Y> getXY(Z z);
public abstract Map<Y, Z> getYZ(X x);
Map<Y, Z> getYZ(X x);
public abstract Map<X, Z> getXZ(Y y);
Map<X, Z> getXZ(Y y);
public abstract List<X> getAllX();
List<X> getAllX();
public abstract List<Y> getAllY();
List<Y> getAllY();
public abstract List<Z> getAllZ();
List<Z> getAllZ();
public abstract void clearXY(Z z);
void clearXY(Z z);
public abstract void clearYZ(X x);
void clearYZ(X x);
public abstract void clearXZ(Y y);
void clearXZ(Y y);
public abstract void clearAll();
void clearAll();
}
@@ -7,7 +7,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Predicate;
public class ConcurrentTable<X, Y, Z> extends AbstractConcurrentTable<X, Y, Z> {
public class ConcurrentTable<X, Y, Z> implements AbstractConcurrentTable<X, Y, Z> {
protected final ConcurrentLinkedDeque<TableEntry<X, Y, Z>> data = new ConcurrentLinkedDeque<>();
protected final boolean flagX;
protected final boolean flagY;
@@ -60,88 +60,88 @@ public class ConcurrentTable<X, Y, Z> extends AbstractConcurrentTable<X, Y, Z> {
@Override
public void remove(X x, Y y, Z z) {
data.removeIf(entry -> entry.getX().equals(x) && entry.getY().equals(y) && entry.getZ().equals(z));
data.removeIf(entry -> entry.x().equals(x) && entry.y().equals(y) && entry.z().equals(z));
}
@Override
public List<Z> getZ(X x, Y y) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getY().equals(y),
TableEntry::getZ
entry -> entry.x().equals(x) && entry.y().equals(y),
TableEntry::z
);
}
@Override
public List<Y> getY(X x, Z z) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getZ().equals(z),
TableEntry::getY
entry -> entry.x().equals(x) && entry.z().equals(z),
TableEntry::y
);
}
@Override
public List<X> getX(Y y, Z z) {
return filterAndCollect(
entry -> entry.getY().equals(y) && entry.getZ().equals(z),
TableEntry::getX
entry -> entry.y().equals(y) && entry.z().equals(z),
TableEntry::x
);
}
@Override
public Map<X, Y> getXY(Z z) {
return filterAndMap(
entry -> entry.getZ().equals(z),
TableEntry::getX,
TableEntry::getY
entry -> entry.z().equals(z),
TableEntry::x,
TableEntry::y
);
}
@Override
public Map<Y, Z> getYZ(X x) {
return filterAndMap(
entry -> entry.getX().equals(x),
TableEntry::getY,
TableEntry::getZ
entry -> entry.x().equals(x),
TableEntry::y,
TableEntry::z
);
}
@Override
public Map<X, Z> getXZ(Y y) {
return filterAndMap(
entry -> entry.getY().equals(y),
TableEntry::getX,
TableEntry::getZ
entry -> entry.y().equals(y),
TableEntry::x,
TableEntry::z
);
}
@Override
public List<X> getAllX() {
return collectAll(TableEntry::getX);
return collectAll(TableEntry::x);
}
@Override
public List<Y> getAllY() {
return collectAll(TableEntry::getY);
return collectAll(TableEntry::y);
}
@Override
public List<Z> getAllZ() {
return collectAll(TableEntry::getZ);
return collectAll(TableEntry::z);
}
@Override
public void clearXY(Z z) {
data.removeIf(entry -> entry.getZ().equals(z));
data.removeIf(entry -> entry.z().equals(z));
}
@Override
public void clearYZ(X x) {
data.removeIf(entry -> entry.getX().equals(x));
data.removeIf(entry -> entry.x().equals(x));
}
@Override
public void clearXZ(Y y) {
data.removeIf(entry -> entry.getY().equals(y));
data.removeIf(entry -> entry.y().equals(y));
}
@Override
@@ -76,9 +76,9 @@ public class OptimizedConcurrentTable<X, Y, Z> extends ConcurrentTable<X, Y, Z>
data.removeIf(entry -> {
boolean shouldRemove = predicate.test(entry);
if (shouldRemove) {
removeFromIndex(xyIndex, entry.getX(), entry.getY(), entry.getZ());
removeFromIndex(yzIndex, entry.getY(), entry.getZ(), entry.getX());
removeFromIndex(zxIndex, entry.getZ(), entry.getX(), entry.getY());
removeFromIndex(xyIndex, entry.x(), entry.y(), entry.z());
removeFromIndex(yzIndex, entry.y(), entry.z(), entry.x());
removeFromIndex(zxIndex, entry.z(), entry.x(), entry.y());
}
return shouldRemove;
});
@@ -86,9 +86,9 @@ public class OptimizedConcurrentTable<X, Y, Z> extends ConcurrentTable<X, Y, Z>
public boolean putIfAbsent(X x, Y y, Z z) {
if (data.stream().anyMatch(entry ->
Objects.equals(entry.getX(), x) &&
Objects.equals(entry.getY(), y) &&
Objects.equals(entry.getZ(), z))) {
Objects.equals(entry.x(), x) &&
Objects.equals(entry.y(), y) &&
Objects.equals(entry.z(), z))) {
return false;
}
put(x, y, z);
@@ -1,25 +1,4 @@
package fun.bm.lophine.utils.concurrent;
public class TableEntry<X, Y, Z> {
private final X x;
private final Y y;
private final Z z;
public TableEntry(X x, Y y, Z z) {
this.x = x;
this.y = y;
this.z = z;
}
public X getX() {
return x;
}
public Y getY() {
return y;
}
public Z getZ() {
return z;
}
public record TableEntry<X, Y, Z>(X x, Y y, Z z) {
}
@@ -50,10 +50,12 @@ import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.event.entity.EntityRemoveEvent;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.event.bot.*;
import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
import org.slf4j.Logger;
import java.util.*;
@@ -190,7 +192,11 @@ public class BotList {
bot.isRealPlayer = true;
bot.loginTime = System.currentTimeMillis();
bot.connection = new ServerBotPacketListenerImpl(this.server, bot);
if (bot.connection.connection.getPlayer() != bot) {
throw new IllegalStateException("Bot connection is not bound to its bot player");
}
bot.connection.markClientLoaded();
bot.getBukkitEntity().setMetadata("NPC", new FixedMetadataValue(MinecraftInternalPlugin.INSTANCE, true));
bot.setServerLevel(world);
BotSpawnLocationEvent event = new BotSpawnLocationEvent(bot.getBukkitEntity(), location);
@@ -315,7 +321,7 @@ public class BotList {
}
}
if (!TickThread.isShutdownThread()) bot.level().getCurrentWorldData().connections.remove(bot.connection.connection);
bot.level().getCurrentWorldData().connections.remove(bot.connection.connection);
bot.level().removePlayerImmediately(bot, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
bot.retireScheduler();
@@ -31,7 +31,12 @@ import org.jetbrains.annotations.Nullable;
public class ServerBotPacketListenerImpl extends ServerGamePacketListenerImpl {
public ServerBotPacketListenerImpl(MinecraftServer server, ServerBot bot) {
super(server, BotConnection.INSTANCE, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
this(server, bot, new BotConnection());
}
private ServerBotPacketListenerImpl(MinecraftServer server, ServerBot bot, BotConnection connection) {
super(server, connection, bot, CommonListenerCookie.createInitial(bot.gameProfile, false));
connection.bind(this);
}
@Override
@@ -53,12 +58,14 @@ public class ServerBotPacketListenerImpl extends ServerGamePacketListenerImpl {
public static class BotConnection extends Connection {
private static final BotConnection INSTANCE = new BotConnection();
public BotConnection() {
super(PacketFlow.SERVERBOUND);
}
private void bind(ServerBotPacketListenerImpl listener) {
this.packetListener = listener;
}
@Override
public void tick() {
}