Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cfc73efc9a | |||
| 0989bcaec7 | |||
| 3763d0b885 | |||
| cd3db4ae8c | |||
| 11e9760eb3 | |||
| 13c5bd7331 |
@@ -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
@@ -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
@@ -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=f4e07a39c69a1f5a55b1d40011ef87265733c402
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ index 0c0775bbb0c0ba0c37d1b884bb106b5250e94750..0dfb25ad032429a2aea7aa441dd5fd18
|
||||
double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
|
||||
if (rangeY != -1) {
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 742ec4321fe42b84f3a9173c4c51563158c6c85f..be6e78317ddcdbb81cdb66b9c9725aae7264561f 100644
|
||||
index 552fd994ee30deb6e9f4228b17a2883cf26b164c..6f7e5d62901733df08c6beafbda2e54bc1c5f917 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -167,7 +167,7 @@ public abstract class Entity
|
||||
|
||||
@@ -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
|
||||
@@ -504,7 +529,7 @@ index ebd2f2887780d9c6c3c44983bac23eab732cf43f..14be3d21cc1a613b1d9164ecc699073f
|
||||
|
||||
public @Nullable ServerPlayer getPlayer(final String playerName) {
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index be6e78317ddcdbb81cdb66b9c9725aae7264561f..d6fb495ffe5fa54e37515f6c5202c9f36bea9470 100644
|
||||
index 6f7e5d62901733df08c6beafbda2e54bc1c5f917..07c5fed7b0bf3ecefb54ccb0bfb922c2f11f7939 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -1257,7 +1257,7 @@ public abstract class Entity
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+3
-3
@@ -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
|
||||
@@ -103,7 +103,7 @@ index 423652d0ccef5c6a3b93480401c3d0f672bbf8a5..65965089d184695588c74294afb2f5c0
|
||||
CrashReport report = CrashReport.forThrowable(var4, "Ticking player");
|
||||
CrashReportCategory category = report.addCategory("Player being ticked");
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index d6fb495ffe5fa54e37515f6c5202c9f36bea9470..a08b78097bb3b549e5c151ee1ed0480042d87d7d 100644
|
||||
index 07c5fed7b0bf3ecefb54ccb0bfb922c2f11f7939..8a92d1748464ef8d1d33e51163a3e0d74120e5a7 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -1459,7 +1459,15 @@ public abstract class Entity
|
||||
|
||||
@@ -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
|
||||
|
||||
+16
-16
@@ -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
|
||||
|
||||
+6
-6
@@ -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();
|
||||
|
||||
|
||||
@@ -417,23 +417,31 @@ public class ServerBot extends ServerPlayer {
|
||||
super.readAdditionalSaveData(nbt);
|
||||
this.setShiftKeyDown(nbt.getBooleanOr("isShiftKeyDown", false));
|
||||
|
||||
CompoundTag createNbt = nbt.read("createStatus", CompoundTag.CODEC).orElseThrow();
|
||||
CompoundTag createNbt = nbt.read("createStatus", CompoundTag.CODEC)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Missing bot createStatus"));
|
||||
String rawName = createNbt.getString("rawName")
|
||||
.orElseGet(() -> createNbt.getString("realName")
|
||||
.orElseThrow(() -> new IllegalArgumentException("Missing bot rawName")));
|
||||
String name = createNbt.getString("name")
|
||||
.orElseThrow(() -> new IllegalArgumentException("Missing bot name"));
|
||||
String skinName = createNbt.getStringOr("skinName", rawName);
|
||||
BotCreateState.Builder createBuilder = BotCreateState
|
||||
.builder(createNbt.getString("rawName")
|
||||
.orElseGet(() -> createNbt.getString("realName")
|
||||
.orElseThrow()), null) // Convert from legacy version, consider to use ca.spottedleaf.dataconverter.minecraft.MCDataConverter instead for release version
|
||||
.name(createNbt.getString("name").orElseThrow());
|
||||
.builder(rawName, null) // Convert from legacy version, consider to use ca.spottedleaf.dataconverter.minecraft.MCDataConverter instead for release version
|
||||
.name(name);
|
||||
|
||||
String[] skin = null;
|
||||
if (createNbt.contains("skin")) {
|
||||
ListTag skinTag = createNbt.getList("skin").orElseThrow();
|
||||
ListTag skinTag = createNbt.getList("skin")
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid bot skin list"));
|
||||
skin = new String[skinTag.size()];
|
||||
for (int i = 0; i < skinTag.size(); i++) {
|
||||
skin[i] = skinTag.getString(i).orElseThrow();
|
||||
final int skinIndex = i;
|
||||
skin[i] = skinTag.getString(i)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid bot skin entry at index " + skinIndex));
|
||||
}
|
||||
}
|
||||
|
||||
createBuilder.skinName(createNbt.getString("skinName").orElseThrow()).skin(skin);
|
||||
createBuilder.skinName(skinName).skin(skin);
|
||||
createBuilder.createReason(BotCreateEvent.CreateReason.INTERNAL).creator(null);
|
||||
|
||||
this.createState = createBuilder.build();
|
||||
@@ -443,11 +451,17 @@ public class ServerBot extends ServerPlayer {
|
||||
if (FakePlayerCompatConfig.fakePlayerReloadAction && nbt.list("actions", CompoundTag.CODEC).isPresent()) {
|
||||
ValueInput.TypedInputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC).orElseThrow();
|
||||
actionNbt.forEach(actionTag -> {
|
||||
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
|
||||
if (action != null) {
|
||||
AbstractBotAction<?> newAction = action.create();
|
||||
newAction.load(actionTag);
|
||||
this.actions.add(newAction);
|
||||
try {
|
||||
String actionName = actionTag.getString("actionName")
|
||||
.orElseThrow(() -> new IllegalArgumentException("Missing actionName"));
|
||||
AbstractBotAction<?> action = Actions.getForName(actionName);
|
||||
if (action != null) {
|
||||
AbstractBotAction<?> newAction = action.create();
|
||||
newAction.load(actionTag);
|
||||
this.actions.add(newAction);
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
LophineLogger.LOGGER.warn("Skipped invalid saved action for bot {}", this.getScoreboardName(), exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -455,10 +469,16 @@ public class ServerBot extends ServerPlayer {
|
||||
if (nbt.list("configs", CompoundTag.CODEC).isPresent()) {
|
||||
ValueInput.TypedInputList<CompoundTag> configNbt = nbt.list("configs", CompoundTag.CODEC).orElseThrow();
|
||||
for (CompoundTag configTag : configNbt) {
|
||||
AbstractBotConfig<?, ?> config = Configs.getConfig(configTag.getString("configName").orElseThrow());
|
||||
if (config != null) {
|
||||
config.setBot(this);
|
||||
config.load(configTag);
|
||||
try {
|
||||
String configName = configTag.getString("configName")
|
||||
.orElseThrow(() -> new IllegalArgumentException("Missing configName"));
|
||||
AbstractBotConfig<?, ?> config = Configs.getConfig(configName);
|
||||
if (config != null) {
|
||||
config.setBot(this);
|
||||
config.load(configTag);
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
LophineLogger.LOGGER.warn("Skipped invalid saved config for bot {}", this.getScoreboardName(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -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() {
|
||||
}
|
||||
|
||||
+37
-7
@@ -45,6 +45,7 @@ import java.util.jar.JarFile;
|
||||
public class LeavesProtocolManager {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
private static final LeavesCustomPayload INVALID_PAYLOAD = new InvalidPayload();
|
||||
|
||||
private static final Map<Class<? extends LeavesCustomPayload>, PayloadReceiverInvokerHolder> PAYLOAD_RECEIVERS = new HashMap<>();
|
||||
private static final Map<Class<? extends LeavesCustomPayload>, Identifier> IDS = new HashMap<>();
|
||||
@@ -217,8 +218,8 @@ public class LeavesProtocolManager {
|
||||
try {
|
||||
return codec.decode(ProtocolUtils.decorate(buf));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to decode payload {}", location, e);
|
||||
throw e;
|
||||
LOGGER.warn("Rejected malformed Leaves payload {}", location, e);
|
||||
return INVALID_PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,9 +239,16 @@ public class LeavesProtocolManager {
|
||||
}
|
||||
|
||||
public static void handlePayload(IdentifierSelector selector, LeavesCustomPayload payload) {
|
||||
if (payload == INVALID_PAYLOAD) {
|
||||
return;
|
||||
}
|
||||
PayloadReceiverInvokerHolder holder;
|
||||
if ((holder = PAYLOAD_RECEIVERS.get(payload.getClass())) != null) {
|
||||
holder.invoke(selector, payload);
|
||||
try {
|
||||
holder.invoke(selector, payload);
|
||||
} catch (RuntimeException exception) {
|
||||
LOGGER.warn("Rejected malformed Leaves payload {} from {}", payload.getClass().getName(), describeSelector(selector), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,22 +256,44 @@ public class LeavesProtocolManager {
|
||||
RegistryFriendlyByteBuf buf1 = ProtocolUtils.decorate(buf);
|
||||
BytebufReceiverInvokerHolder holder;
|
||||
if ((holder = STRICT_BYTEBUF_RECEIVERS.get(location.toString())) != null) {
|
||||
holder.invoke(selector, buf1);
|
||||
safeInvokeBytebuf(holder, selector, location, buf1);
|
||||
return true;
|
||||
}
|
||||
if ((holder = NAMESPACED_BYTEBUF_RECEIVERS.get(location.getNamespace())) != null) {
|
||||
if (holder.invoke(selector, buf1)) {
|
||||
if (safeInvokeBytebuf(holder, selector, location, buf1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (var holder1 : GENERIC_BYTEBUF_RECEIVERS) {
|
||||
if (holder1.invoke(selector, buf1)) {
|
||||
if (safeInvokeBytebuf(holder1, selector, location, buf1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean safeInvokeBytebuf(BytebufReceiverInvokerHolder holder, IdentifierSelector selector, Identifier location, RegistryFriendlyByteBuf buf) {
|
||||
try {
|
||||
return holder.invoke(selector, buf);
|
||||
} catch (RuntimeException exception) {
|
||||
LOGGER.warn("Rejected malformed bytebuf payload {} from {}", location, describeSelector(selector), exception);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static String describeSelector(IdentifierSelector selector) {
|
||||
if (selector.player() != null) {
|
||||
return selector.player().getScoreboardName();
|
||||
}
|
||||
if (selector.context() != null) {
|
||||
return selector.context().profile().name();
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private record InvalidPayload() implements LeavesCustomPayload {
|
||||
}
|
||||
|
||||
public static void handleTick() {
|
||||
long currentTime = System.currentTimeMillis() / MinecraftServer.getServer().tickRateManager().nanosecondsPerTick();
|
||||
if (currentTime == lastAcceptTime) return;
|
||||
@@ -414,4 +444,4 @@ public class LeavesProtocolManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-9
@@ -400,15 +400,25 @@ public class REIServerProtocol implements LeavesProtocol {
|
||||
private static List<List<ItemStack>> readInputs(RegistryAccess registryAccess, ListTag tag) {
|
||||
List<List<ItemStack>> items = new ArrayList<>();
|
||||
for (Tag t : tag) {
|
||||
CompoundTag compoundTag = (CompoundTag) t;
|
||||
compoundTag.getInt("Index").orElseThrow();
|
||||
if (!(t instanceof CompoundTag compoundTag)) {
|
||||
throw new IllegalStateException("Invalid REI input entry");
|
||||
}
|
||||
if (compoundTag.getInt("Index").isEmpty()) {
|
||||
throw new IllegalStateException("Missing REI input index");
|
||||
}
|
||||
ListTag ingredientList = compoundTag.getListOrEmpty("Ingredient");
|
||||
List<ItemStack> slotItems = new ArrayList<>();
|
||||
for (Tag ingredient : ingredientList) {
|
||||
CompoundTag ingredientTag = (CompoundTag) ingredient;
|
||||
if (!(ingredient instanceof CompoundTag ingredientTag)) {
|
||||
throw new IllegalStateException("Invalid REI ingredient entry");
|
||||
}
|
||||
Tag value = ingredientTag.get("value");
|
||||
if (value == null) {
|
||||
throw new IllegalStateException("Missing REI ingredient value");
|
||||
}
|
||||
ItemStack stack = ItemStack.OPTIONAL_CODEC.parse(
|
||||
registryAccess.createSerializationContext(NbtOps.INSTANCE),
|
||||
ingredientTag.get("value")
|
||||
value
|
||||
).getOrThrow();
|
||||
slotItems.add(stack);
|
||||
}
|
||||
@@ -420,16 +430,28 @@ public class REIServerProtocol implements LeavesProtocol {
|
||||
private static List<SlotAccessor> readSlots(AbstractContainerMenu menu, ServerPlayer player, ListTag tag) {
|
||||
List<SlotAccessor> slots = new ArrayList<>();
|
||||
for (Tag t : tag) {
|
||||
CompoundTag compoundTag = (CompoundTag) t;
|
||||
String id = compoundTag.getString("id").orElseThrow();
|
||||
if (!(t instanceof CompoundTag compoundTag)) {
|
||||
throw new IllegalStateException("Invalid REI slot entry");
|
||||
}
|
||||
String id = compoundTag.getString("id").orElseThrow(() -> new IllegalStateException("Missing REI slot id"));
|
||||
if (!id.startsWith(PROTOCOL_ID + ":")) {
|
||||
throw new IllegalStateException("Invalid slot id: " + id + ", expected to start with '" + PROTOCOL_ID + ":'");
|
||||
}
|
||||
id = id.substring((PROTOCOL_ID + ":").length());
|
||||
int slot = compoundTag.getInt("Slot").orElseThrow();
|
||||
int slot = compoundTag.getInt("Slot").orElseThrow(() -> new IllegalStateException("Missing REI slot index"));
|
||||
SlotAccessor accessor = switch (id) {
|
||||
case "vanilla" -> new VanillaSlotAccessor(menu.slots.get(slot));
|
||||
case "player" -> new PlayerInventorySlotAccessor(player, slot);
|
||||
case "vanilla" -> {
|
||||
if (slot < 0 || slot >= menu.slots.size()) {
|
||||
throw new IllegalStateException("Invalid vanilla slot index: " + slot);
|
||||
}
|
||||
yield new VanillaSlotAccessor(menu.slots.get(slot));
|
||||
}
|
||||
case "player" -> {
|
||||
if (slot < 0 || slot >= player.getInventory().getContainerSize()) {
|
||||
throw new IllegalStateException("Invalid player slot index: " + slot);
|
||||
}
|
||||
yield new PlayerInventorySlotAccessor(player, slot);
|
||||
}
|
||||
default -> throw new IllegalStateException("Unknown container id: " + id);
|
||||
};
|
||||
slots.add(accessor);
|
||||
|
||||
+23
-2
@@ -51,17 +51,21 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
|
||||
|
||||
public static final int MINECRAFT_DATA_VERSION = SharedConstants.getProtocolVersion();
|
||||
public static final int SCHEMATIC_VERSION = 7;
|
||||
private static final long MAX_REGION_VOLUME = 16_777_216L;
|
||||
|
||||
@NotNull
|
||||
@Contract("_ -> new")
|
||||
public static LitematicaSchematic readFromNBT(@NotNull CompoundTag nbt) {
|
||||
if (nbt.contains("Version")) {
|
||||
final int version = nbt.getIntOr("Version", -1);
|
||||
final int minecraftDataVersion = nbt.contains("MinecraftDataVersion") ? nbt.getInt("MinecraftDataVersion").orElseThrow() : SharedConstants.getProtocolVersion();
|
||||
final int minecraftDataVersion = nbt.getIntOr("MinecraftDataVersion", SharedConstants.getProtocolVersion());
|
||||
|
||||
if (version >= 1 && version <= SCHEMATIC_VERSION) {
|
||||
SchematicMetadata metadata = SchematicMetadata.readFromNBT(nbt.getCompoundOrEmpty("Metadata"), version, minecraftDataVersion, FileType.LITEMATICA_SCHEMATIC);
|
||||
Map<String, SubRegion> subRegions = readSubRegionsFromNBT(nbt.getCompoundOrEmpty("Regions"), version, minecraftDataVersion);
|
||||
if (subRegions.isEmpty()) {
|
||||
throw new IllegalArgumentException("Schematic has no regions");
|
||||
}
|
||||
return new LitematicaSchematic(subRegions, metadata);
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported or future schematic version");
|
||||
@@ -116,6 +120,7 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
|
||||
if (position == null || size == null) {
|
||||
throw new IllegalArgumentException("Invalid region");
|
||||
}
|
||||
validateRegionSize(size);
|
||||
|
||||
Map<BlockPos, CompoundTag> tileEntities;
|
||||
List<EntityInfo> entities;
|
||||
@@ -141,6 +146,9 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
|
||||
Tag blockState = regionTag.get("BlockStates");
|
||||
if (blockState != null && blockState.getId() == Tag.TAG_LONG_ARRAY) {
|
||||
ListTag palette = regionTag.getListOrEmpty("BlockStatePalette");
|
||||
if (palette.isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing block state palette");
|
||||
}
|
||||
long[] blockStateArr = ((LongArrayTag) blockState).getAsLongArray();
|
||||
BlockPos posEndRel = PositionUtils.getRelativeEndPositionFromAreaSize(size).offset(position);
|
||||
BlockPos posMin = PositionUtils.getMinCorner(position, posEndRel);
|
||||
@@ -155,6 +163,19 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
|
||||
return new SubRegion(blockContainers, tileEntities, pendingBlockTicks, pendingFluidTicks, entities, position, size);
|
||||
}
|
||||
|
||||
private static void validateRegionSize(BlockPos size) {
|
||||
long x = Math.abs((long) size.getX());
|
||||
long y = Math.abs((long) size.getY());
|
||||
long z = Math.abs((long) size.getZ());
|
||||
if (x == 0 || y == 0 || z == 0) {
|
||||
throw new IllegalArgumentException("Region has zero size");
|
||||
}
|
||||
long volume = Math.multiplyExact(Math.multiplyExact(x, y), z);
|
||||
if (volume > MAX_REGION_VOLUME) {
|
||||
throw new IllegalArgumentException("Region volume too large: " + volume);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<EntityInfo> readEntitiesFromNBT(ListTag tagList) {
|
||||
List<EntityInfo> entityList = new ArrayList<>();
|
||||
final int size = tagList.size();
|
||||
@@ -278,4 +299,4 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
|
||||
this.nbt = nbt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-7
@@ -264,12 +264,17 @@ public class ServuxLitematicsProtocol implements LeavesProtocol {
|
||||
}
|
||||
|
||||
if (tags.getStringOr("Task", "").equals("LitematicaPaste")) {
|
||||
ServuxProtocol.LOGGER.debug("litematic_data: Servux Paste request from player {}", player.getName().getString());
|
||||
ServerLevel serverLevel = player.level();
|
||||
long timeStart = System.currentTimeMillis();
|
||||
SchematicPlacement placement = SchematicPlacement.createFromNbt(tags);
|
||||
ReplaceBehavior replaceMode = ReplaceBehavior.fromStringStatic(tags.getStringOr("ReplaceMode", ReplaceBehavior.NONE.name()));
|
||||
placement.pasteTo(serverLevel, replaceMode, player, timeStart);
|
||||
try {
|
||||
ServuxProtocol.LOGGER.debug("litematic_data: Servux Paste request from player {}", player.getName().getString());
|
||||
ServerLevel serverLevel = player.level();
|
||||
long timeStart = System.currentTimeMillis();
|
||||
SchematicPlacement placement = SchematicPlacement.createFromNbt(tags);
|
||||
ReplaceBehavior replaceMode = ReplaceBehavior.fromStringStatic(tags.getStringOr("ReplaceMode", ReplaceBehavior.NONE.name()));
|
||||
placement.pasteTo(serverLevel, replaceMode, player, timeStart);
|
||||
} catch (RuntimeException exception) {
|
||||
player.getBukkitEntity().sendActionBar(Component.text("Invalid Litematica paste data", NamedTextColor.RED));
|
||||
ServuxProtocol.LOGGER.warn("Rejected invalid Litematica paste request from {}", player.getScoreboardName(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,4 +443,4 @@ public class ServuxLitematicsProtocol implements LeavesProtocol {
|
||||
return !this.hasBuffer() && !this.hasNbt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-2
@@ -47,11 +47,19 @@ public class LitematicaBitArray {
|
||||
|
||||
public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn) {
|
||||
Validate.inclusiveBetween(1L, 32L, bitsPerEntryIn);
|
||||
Validate.isTrue(arraySizeIn >= 0L, "arraySize must not be negative");
|
||||
this.arraySize = arraySizeIn;
|
||||
this.bitsPerEntry = bitsPerEntryIn;
|
||||
this.maxEntryValue = (1L << bitsPerEntryIn) - 1L;
|
||||
|
||||
this.longArray = Objects.requireNonNullElseGet(longArrayIn, () -> new long[(int) (roundUp(arraySizeIn * bitsPerEntryIn, 64L) / 64L)]);
|
||||
long backingLength = roundUp(Math.multiplyExact(arraySizeIn, bitsPerEntryIn), 64L) / 64L;
|
||||
Validate.isTrue(backingLength <= Integer.MAX_VALUE, "BitArray backing storage is too large");
|
||||
if (longArrayIn != null) {
|
||||
Validate.isTrue(longArrayIn.length >= backingLength, "BitArray backing storage is too small");
|
||||
this.longArray = longArrayIn;
|
||||
} else {
|
||||
this.longArray = new long[(int) backingLength];
|
||||
}
|
||||
}
|
||||
|
||||
public static long roundUp(long value, long interval) {
|
||||
@@ -71,6 +79,7 @@ public class LitematicaBitArray {
|
||||
}
|
||||
|
||||
public void setAt(long index, int value) {
|
||||
this.checkIndex(index);
|
||||
long startOffset = index * (long) this.bitsPerEntry;
|
||||
int startArrIndex = (int) (startOffset >> 6); // startOffset / 64
|
||||
int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6);
|
||||
@@ -85,6 +94,7 @@ public class LitematicaBitArray {
|
||||
}
|
||||
|
||||
public int getAt(long index) {
|
||||
this.checkIndex(index);
|
||||
long startOffset = index * (long) this.bitsPerEntry;
|
||||
int startArrIndex = (int) (startOffset >> 6); // startOffset / 64
|
||||
int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6);
|
||||
@@ -101,4 +111,10 @@ public class LitematicaBitArray {
|
||||
public long size() {
|
||||
return this.arraySize;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIndex(long index) {
|
||||
if (index < 0L || index >= this.arraySize) {
|
||||
throw new IndexOutOfBoundsException("BitArray index " + index + " out of bounds for length " + this.arraySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -49,7 +49,9 @@ public interface LitematicaBlockStatePalette {
|
||||
final int size = tagList.size();
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
CompoundTag tag = tagList.getCompound(i).orElseThrow();
|
||||
final int paletteIndex = i;
|
||||
CompoundTag tag = tagList.getCompound(i)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid block state palette entry at index " + paletteIndex));
|
||||
BlockState state = NbtUtils.readBlockState(lookup, tag);
|
||||
|
||||
if (i > 0 || state != LitematicaBlockStateContainer.AIR_BLOCK_STATE) {
|
||||
@@ -59,4 +61,4 @@ public interface LitematicaBlockStatePalette {
|
||||
}
|
||||
|
||||
ListTag writeToNBT();
|
||||
}
|
||||
}
|
||||
|
||||
+31
-7
@@ -62,20 +62,31 @@ public class SchematicPlacement {
|
||||
}
|
||||
|
||||
public static SchematicPlacement createFromNbt(CompoundTag tags) {
|
||||
if (!tags.contains("Schematics")) {
|
||||
throw new IllegalArgumentException("Missing Schematics tag");
|
||||
}
|
||||
BlockPos origin = NbtUtils.readBlockPosFromArrayTag(tags, "Origin");
|
||||
if (origin == null) {
|
||||
throw new IllegalArgumentException("Missing or invalid Origin tag");
|
||||
}
|
||||
SchematicPlacement placement = new SchematicPlacement(
|
||||
LitematicaSchematic.readFromNBT(tags.getCompoundOrEmpty("Schematics")),
|
||||
NbtUtils.readBlockPosFromArrayTag(tags, "Origin"),
|
||||
origin,
|
||||
tags.getStringOr("Name", "")
|
||||
);
|
||||
placement.mirror = Mirror.values()[tags.getIntOr("Mirror", 0)];
|
||||
placement.rotation = Rotation.values()[tags.getIntOr("Rotation", 0)];
|
||||
placement.mirror = enumByOrdinal(Mirror.values(), tags.getIntOr("Mirror", 0), "Mirror");
|
||||
placement.rotation = enumByOrdinal(Rotation.values(), tags.getIntOr("Rotation", 0), "Rotation");
|
||||
for (String name : tags.getCompoundOrEmpty("SubRegions").keySet()) {
|
||||
CompoundTag compound = tags.getCompoundOrEmpty("SubRegions").getCompoundOrEmpty(name);
|
||||
BlockPos pos = NbtUtils.readBlockPosFromArrayTag(compound, "Pos");
|
||||
if (pos == null) {
|
||||
throw new IllegalArgumentException("Missing or invalid Pos tag for sub-region " + name);
|
||||
}
|
||||
var sub = new SubRegionPlacement(
|
||||
compound.getStringOr("Name", "?"),
|
||||
NbtUtils.readBlockPosFromArrayTag(compound, "Pos"),
|
||||
Rotation.values()[compound.getIntOr("Rotation", 0)],
|
||||
Mirror.values()[compound.getIntOr("Mirror", 0)],
|
||||
pos,
|
||||
enumByOrdinal(Rotation.values(), compound.getIntOr("Rotation", 0), "SubRegions." + name + ".Rotation"),
|
||||
enumByOrdinal(Mirror.values(), compound.getIntOr("Mirror", 0), "SubRegions." + name + ".Mirror"),
|
||||
compound.getBooleanOr("Enabled", true),
|
||||
compound.getBooleanOr("IgnoreEntities", false)
|
||||
);
|
||||
@@ -84,6 +95,13 @@ public class SchematicPlacement {
|
||||
return placement;
|
||||
}
|
||||
|
||||
private static <T> T enumByOrdinal(T[] values, int ordinal, String tagName) {
|
||||
if (ordinal < 0 || ordinal >= values.length) {
|
||||
throw new IllegalArgumentException("Invalid " + tagName + " ordinal: " + ordinal);
|
||||
}
|
||||
return values[ordinal];
|
||||
}
|
||||
|
||||
public static IntBoundingBox getBoundsWithinChunkForBox(Box box, int chunkX, int chunkZ) {
|
||||
final int chunkXMin = chunkX << 4;
|
||||
final int chunkZMin = chunkZ << 4;
|
||||
@@ -302,7 +320,13 @@ public class SchematicPlacement {
|
||||
AtomicInteger count_full = new AtomicInteger();
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
|
||||
streamChunkPos(Objects.requireNonNull(enclosingBox.toVanilla())).forEach(chunkPos -> {
|
||||
BlockBox vanillaBox = enclosingBox.toVanilla();
|
||||
if (vanillaBox == null) {
|
||||
ServuxProtocol.LOGGER.error("receiver a null vanilla enclosing box");
|
||||
return;
|
||||
}
|
||||
|
||||
streamChunkPos(vanillaBox).forEach(chunkPos -> {
|
||||
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
serverWorld,
|
||||
chunkPos.x(),
|
||||
|
||||
+19
-4
@@ -51,6 +51,7 @@ public class CommunicationManager implements LeavesProtocol {
|
||||
protected static final Map<UUID, Exchange> modifyState = new ConcurrentHashMap<>();
|
||||
protected static final Rotation[] rotOrdinals = Rotation.values();
|
||||
protected static final Mirror[] mirOrdinals = Mirror.values();
|
||||
private static final int MAX_SUB_REGION_MODIFICATIONS = 4096;
|
||||
private static final Map<UUID, List<ServerPlacement>> downloadingFile = new ConcurrentHashMap<>();
|
||||
private static final Map<ExchangeTarget, ServerPlayer> playerMap = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -87,7 +88,11 @@ public class CommunicationManager implements LeavesProtocol {
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = SyncmaticaPayload.class)
|
||||
public static void onPacketGet(ServerPlayer player, SyncmaticaPayload payload) {
|
||||
onPacket(player.connection.exchangeTarget, payload.packetType(), payload.data());
|
||||
try {
|
||||
onPacket(player.connection.exchangeTarget, payload.packetType(), payload.data());
|
||||
} catch (IllegalArgumentException | IndexOutOfBoundsException exception) {
|
||||
LOGGER.warn("Rejected malformed Syncmatica packet {} from {}", payload.packetType(), player.getScoreboardName(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onPacket(final @NotNull ExchangeTarget source, final Identifier id, final FriendlyByteBuf packetBuf) {
|
||||
@@ -326,20 +331,30 @@ public class CommunicationManager implements LeavesProtocol {
|
||||
public static void receivePositionData(final @NotNull ServerPlacement placement, final @NotNull FriendlyByteBuf buf, final @NotNull ExchangeTarget exchangeTarget) {
|
||||
final BlockPos pos = buf.readBlockPos();
|
||||
final String dimensionId = buf.readUtf(32767);
|
||||
final Rotation rot = rotOrdinals[buf.readInt()];
|
||||
final Mirror mir = mirOrdinals[buf.readInt()];
|
||||
final Rotation rot = readOrdinal(rotOrdinals, buf.readInt(), "rotation");
|
||||
final Mirror mir = readOrdinal(mirOrdinals, buf.readInt(), "mirror");
|
||||
placement.move(dimensionId, pos, rot, mir);
|
||||
|
||||
if (exchangeTarget.getFeatureSet().hasFeature(Feature.CORE_EX)) {
|
||||
final SubRegionData subRegionData = placement.getSubRegionData();
|
||||
subRegionData.reset();
|
||||
final int limit = buf.readInt();
|
||||
if (limit < 0 || limit > MAX_SUB_REGION_MODIFICATIONS) {
|
||||
throw new IllegalArgumentException("Invalid sub-region modification count: " + limit);
|
||||
}
|
||||
for (int i = 0; i < limit; i++) {
|
||||
subRegionData.modify(buf.readUtf(32767), buf.readBlockPos(), rotOrdinals[buf.readInt()], mirOrdinals[buf.readInt()]);
|
||||
subRegionData.modify(buf.readUtf(32767), buf.readBlockPos(), readOrdinal(rotOrdinals, buf.readInt(), "sub-region rotation"), readOrdinal(mirOrdinals, buf.readInt(), "sub-region mirror"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T readOrdinal(T[] values, int ordinal, String name) {
|
||||
if (ordinal < 0 || ordinal >= values.length) {
|
||||
throw new IllegalArgumentException("Invalid " + name + " ordinal: " + ordinal);
|
||||
}
|
||||
return values[ordinal];
|
||||
}
|
||||
|
||||
public static void download(final ServerPlacement syncmatic, final ExchangeTarget source) throws NoSuchAlgorithmException, IOException {
|
||||
if (!SyncmaticaProtocol.getFileStorage().getLocalState(syncmatic).isReadyForDownload()) {
|
||||
throw new IllegalArgumentException(syncmatic.toString() + " is not ready for download local state is: " + SyncmaticaProtocol.getFileStorage().getLocalState(syncmatic).toString());
|
||||
|
||||
Reference in New Issue
Block a user