mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
Readd Leaves protocols
This commit is contained in:
@@ -1,138 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Thu, 3 Aug 2023 20:36:38 +0800
|
||||
Subject: [PATCH] Leaves: Replay Mod API
|
||||
|
||||
Co-authored-by: alazeprt <nono135246@126.com>
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
This patch is Powered by ReplayMod(https://github.com/ReplayMod)
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
|
||||
index f4e1c330b6e23ac9edde5c5644a0a41bdfde81f9..917b7cbd7647fccd9bdd70544bb0e5b6532045a7 100644
|
||||
--- a/src/main/java/org/bukkit/Bukkit.java
|
||||
+++ b/src/main/java/org/bukkit/Bukkit.java
|
||||
@@ -3191,4 +3191,10 @@ public final class Bukkit {
|
||||
server.clearBlockHighlights();
|
||||
}
|
||||
// Purpur end - Debug Marker API
|
||||
+
|
||||
+ // Leaves start - Photographer API
|
||||
+ public static @NotNull org.leavesmc.leaves.entity.PhotographerManager getPhotographerManager() {
|
||||
+ return server.getPhotographerManager();
|
||||
+ }
|
||||
+ // Leaves end - Photographer API
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
|
||||
index 50984d397d9ff98e82d02efc166aba61f645e491..a1b76899a416b997c6844bcaf837219ce0726496 100644
|
||||
--- a/src/main/java/org/bukkit/Server.java
|
||||
+++ b/src/main/java/org/bukkit/Server.java
|
||||
@@ -2902,4 +2902,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
|
||||
*/
|
||||
void clearBlockHighlights();
|
||||
// Purpur end - Debug Marker API
|
||||
+
|
||||
+ // Leaves start - Photographer API
|
||||
+ @NotNull org.leavesmc.leaves.entity.PhotographerManager getPhotographerManager();
|
||||
+ // Leaves end - Photographer API
|
||||
}
|
||||
diff --git a/src/main/java/org/leavesmc/leaves/entity/Photographer.java b/src/main/java/org/leavesmc/leaves/entity/Photographer.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..5b564dfd8aa882d0dc8b1833a4b46e1bba699876
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/leavesmc/leaves/entity/Photographer.java
|
||||
@@ -0,0 +1,27 @@
|
||||
+package org.leavesmc.leaves.entity;
|
||||
+
|
||||
+import org.bukkit.entity.Player;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.jetbrains.annotations.Nullable;
|
||||
+
|
||||
+import java.io.File;
|
||||
+
|
||||
+public interface Photographer extends Player {
|
||||
+
|
||||
+ @NotNull
|
||||
+ public String getId();
|
||||
+
|
||||
+ public void setRecordFile(@NotNull File file);
|
||||
+
|
||||
+ public void stopRecording();
|
||||
+
|
||||
+ public void stopRecording(boolean async);
|
||||
+
|
||||
+ public void stopRecording(boolean async, boolean save);
|
||||
+
|
||||
+ public void pauseRecording();
|
||||
+
|
||||
+ public void resumeRecording();
|
||||
+
|
||||
+ public void setFollowPlayer(@Nullable Player player);
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/main/java/org/leavesmc/leaves/entity/PhotographerManager.java b/src/main/java/org/leavesmc/leaves/entity/PhotographerManager.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ccb19e75748803eb9ad356ffcd0ccfd5145ed776
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/leavesmc/leaves/entity/PhotographerManager.java
|
||||
@@ -0,0 +1,32 @@
|
||||
+package org.leavesmc.leaves.entity;
|
||||
+
|
||||
+import org.bukkit.Location;
|
||||
+import org.bukkit.util.Consumer;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.jetbrains.annotations.Nullable;
|
||||
+import org.leavesmc.leaves.replay.BukkitRecorderOption;
|
||||
+
|
||||
+import java.util.Collection;
|
||||
+import java.util.UUID;
|
||||
+
|
||||
+public interface PhotographerManager {
|
||||
+ @Nullable
|
||||
+ public Photographer getPhotographer(@NotNull UUID uuid);
|
||||
+
|
||||
+ @Nullable
|
||||
+ public Photographer getPhotographer(@NotNull String id);
|
||||
+
|
||||
+ @Nullable
|
||||
+ public Photographer createPhotographer(@NotNull String id, @NotNull Location location);
|
||||
+
|
||||
+ @Nullable
|
||||
+ public Photographer createPhotographer(@NotNull String id, @NotNull Location location, @NotNull BukkitRecorderOption recorderOption);
|
||||
+
|
||||
+ public void removePhotographer(@NotNull String id);
|
||||
+
|
||||
+ public void removePhotographer(@NotNull UUID uuid);
|
||||
+
|
||||
+ public void removeAllPhotographers();
|
||||
+
|
||||
+ public Collection<Photographer> getPhotographers();
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/main/java/org/leavesmc/leaves/replay/BukkitRecorderOption.java b/src/main/java/org/leavesmc/leaves/replay/BukkitRecorderOption.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c985721bdf6be0b8a154e7abfd50e0168965a8d1
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/org/leavesmc/leaves/replay/BukkitRecorderOption.java
|
||||
@@ -0,0 +1,18 @@
|
||||
+package org.leavesmc.leaves.replay;
|
||||
+
|
||||
+public class BukkitRecorderOption {
|
||||
+
|
||||
+ // public int recordDistance = -1;
|
||||
+ public String serverName = "Leaf";
|
||||
+ public BukkitRecordWeather forceWeather = BukkitRecordWeather.NULL;
|
||||
+ public int forceDayTime = -1;
|
||||
+ public boolean ignoreChat = false;
|
||||
+ // public boolean ignoreItem = false;
|
||||
+
|
||||
+ public enum BukkitRecordWeather {
|
||||
+ CLEAR,
|
||||
+ RAIN,
|
||||
+ THUNDER,
|
||||
+ NULL
|
||||
+ }
|
||||
+}
|
||||
\ No newline at end of file
|
||||
@@ -1,100 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Tue, 26 Sep 2023 19:00:41 +0800
|
||||
Subject: [PATCH] Leaves: Protocol Core
|
||||
|
||||
TODO: Check whether Leaves's Return-nether-portal-fix.patch improves performance
|
||||
and change store way to sql maybe?
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
Commit: 99b3aafce1f162c68a771fe56d77f33648636b7d
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java
|
||||
index fb263fa1f30a7dfcb7ec2656abfb38e5fe88eac9..7e19dfe90a63ff26f03b95891dacb7360bba5a3c 100644
|
||||
--- a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java
|
||||
+++ b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java
|
||||
@@ -40,13 +40,23 @@ public interface CustomPacketPayload {
|
||||
|
||||
@Override
|
||||
public void encode(B buffer, CustomPacketPayload value) {
|
||||
+ // Leaves start - protocol core
|
||||
+ if (value instanceof org.leavesmc.leaves.protocol.core.LeavesCustomPayload<?> payload) {
|
||||
+ buffer.writeResourceLocation(payload.id());
|
||||
+ payload.write(buffer);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Leaves end - protocol core
|
||||
this.writeCap(buffer, value.type(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomPacketPayload decode(B buffer) {
|
||||
ResourceLocation resourceLocation = buffer.readResourceLocation();
|
||||
- return (CustomPacketPayload)this.findCodec(resourceLocation).decode(buffer);
|
||||
+ // Leaves start - protocol core
|
||||
+ var payload = org.leavesmc.leaves.protocol.core.LeavesProtocolManager.decode(resourceLocation, buffer);
|
||||
+ return java.util.Objects.requireNonNullElseGet(payload, () -> this.findCodec(resourceLocation).decode(buffer));
|
||||
+ // Leaves end - protocol core
|
||||
}
|
||||
};
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 047a09cf4a2c32e714aacedeccb0928ef2c7dfa9..dddbb18992348fb7e8a6552423d134809cd7fdbc 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1747,6 +1747,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
GameTestTicker.SINGLETON.tick();
|
||||
}
|
||||
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleTick(); // Leaves - protocol
|
||||
+
|
||||
for (int i = 0; i < this.tickables.size(); i++) {
|
||||
this.tickables.get(i).run();
|
||||
}
|
||||
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
index 921ad69b699f693e3dfc8d912b0f1a05d8f81743..285af1576d6bef09f094b7e990b5bcd6eafda71f 100644
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -151,6 +151,11 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
|
||||
@Override
|
||||
public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
|
||||
+ // Leaves start - protocol
|
||||
+ if (packet.payload() instanceof org.leavesmc.leaves.protocol.core.LeavesCustomPayload<?> leavesPayload) {
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePayload(player, leavesPayload);
|
||||
+ }
|
||||
+ // Leaves end - protocol
|
||||
// Paper start
|
||||
if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload(String brand)) {
|
||||
this.player.clientBrandName = brand;
|
||||
@@ -210,6 +215,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
final String channel = new String(data, from, length, java.nio.charset.StandardCharsets.US_ASCII);
|
||||
if (register) {
|
||||
this.getCraftPlayer().addChannel(channel);
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleMinecraftRegister(channel, player); // Leaves - protocol
|
||||
} else {
|
||||
this.getCraftPlayer().removeChannel(channel);
|
||||
}
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 9a2f2dc1eb471776de6049590cb16e8a061aa24e..e0dbafdbf36ab8597827ac7a828981013ec16bde 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -341,6 +341,8 @@ public abstract class PlayerList {
|
||||
|
||||
player.didPlayerJoinEvent = true; // Gale - EMC - do not process chat/commands before player has joined
|
||||
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol
|
||||
+
|
||||
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
|
||||
|
||||
if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure
|
||||
@@ -518,6 +520,7 @@ public abstract class PlayerList {
|
||||
return this.remove(player, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? player.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(player.getDisplayName())));
|
||||
}
|
||||
public net.kyori.adventure.text.Component remove(ServerPlayer player, net.kyori.adventure.text.Component leaveMessage) {
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerLeave(player); // Leaves - protocol
|
||||
// Paper end - Fix kick event leave message not being sent
|
||||
org.purpurmc.purpur.task.BossBarTask.removeFromAll(player.getBukkitEntity()); // Purpur - Implement TPSBar
|
||||
ServerLevel serverLevel = player.serverLevel();
|
||||
@@ -1,114 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Sat, 3 Dec 2022 08:57:15 +0800
|
||||
Subject: [PATCH] Leaves: Jade Protocol
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
This patch is Powered by Jade (https://github.com/Snownee/Jade)
|
||||
|
||||
diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
||||
index a24ed1747fb8836927ac41b822dc666862701516..d840577023d42dc986e2b811382dfc433083ffb3 100644
|
||||
--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
||||
+++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
||||
@@ -59,7 +59,7 @@ public class Armadillo extends Animal {
|
||||
public final AnimationState rollOutAnimationState = new AnimationState();
|
||||
public final AnimationState rollUpAnimationState = new AnimationState();
|
||||
public final AnimationState peekAnimationState = new AnimationState();
|
||||
- private int scuteTime;
|
||||
+ public int scuteTime; // Leaves - private -> public
|
||||
private boolean peekReceivedClient = false;
|
||||
|
||||
public Armadillo(EntityType<? extends Animal> entityType, Level level) {
|
||||
diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java
|
||||
index ec9db1c12426db80dbf02d704e6c7ec867d59f6f..0fdda6b24aee95170e54079e53125b4aed19ac0b 100644
|
||||
--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
|
||||
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
|
||||
@@ -285,7 +285,7 @@ public class Tadpole extends AbstractFish {
|
||||
}
|
||||
}
|
||||
|
||||
- private int getTicksLeftUntilAdult() {
|
||||
+ public int getTicksLeftUntilAdult() { // Leaves - private -> public
|
||||
return Math.max(0, ticksToBeFrog - this.age);
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/storage/loot/LootPool.java b/net/minecraft/world/level/storage/loot/LootPool.java
|
||||
index 29ad43245a310756c4227acd7532e905f7f8b8ee..ad422817593449b8e914628b51d760e732e2d50c 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/LootPool.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/LootPool.java
|
||||
@@ -36,7 +36,7 @@ public class LootPool {
|
||||
)
|
||||
.apply(instance, LootPool::new)
|
||||
);
|
||||
- private final List<LootPoolEntryContainer> entries;
|
||||
+ public final List<LootPoolEntryContainer> entries; // Leaves - private -> public
|
||||
private final List<LootItemCondition> conditions;
|
||||
private final Predicate<LootContext> compositeCondition;
|
||||
private final List<LootItemFunction> functions;
|
||||
diff --git a/net/minecraft/world/level/storage/loot/LootTable.java b/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
index dd646b11ef0a40e7f782742e62ccccfa9bcfd235..c9820777342124524c046d910085b1bf89c12488 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
@@ -45,7 +45,7 @@ public class LootTable {
|
||||
public static final Codec<Holder<LootTable>> CODEC = RegistryFileCodec.create(Registries.LOOT_TABLE, DIRECT_CODEC);
|
||||
private final ContextKeySet paramSet;
|
||||
private final Optional<ResourceLocation> randomSequence;
|
||||
- private final List<LootPool> pools;
|
||||
+ public final List<LootPool> pools; // Leaves - private -> public
|
||||
private final List<LootItemFunction> functions;
|
||||
private final BiFunction<ItemStack, LootContext, ItemStack> compositeFunction;
|
||||
public org.bukkit.craftbukkit.CraftLootTable craftLootTable; // CraftBukkit
|
||||
diff --git a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
|
||||
index 8e91ddc6c0e492d165ad8322b4a3d5c3bad5409c..6e420bfb3c223b094157bdfec7dad20d8eab4968 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
|
||||
@@ -9,7 +9,7 @@ import net.minecraft.world.level.storage.loot.ValidationContext;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
|
||||
public abstract class CompositeEntryBase extends LootPoolEntryContainer {
|
||||
- protected final List<LootPoolEntryContainer> children;
|
||||
+ public final List<LootPoolEntryContainer> children; // Leaves - private -> public
|
||||
private final ComposableEntryContainer composedChildren;
|
||||
|
||||
protected CompositeEntryBase(List<LootPoolEntryContainer> children, List<LootItemCondition> conditions) {
|
||||
diff --git a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
|
||||
index e0e933245e038b7229eeddbda272b081161ab603..c5e3834fa970ac909cefea43420378394153d781 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
|
||||
@@ -13,7 +13,7 @@ import net.minecraft.world.level.storage.loot.predicates.ConditionUserBuilder;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
|
||||
public abstract class LootPoolEntryContainer implements ComposableEntryContainer {
|
||||
- protected final List<LootItemCondition> conditions;
|
||||
+ public final List<LootItemCondition> conditions; // Leaves - private -> public
|
||||
private final Predicate<LootContext> compositeCondition;
|
||||
|
||||
protected LootPoolEntryContainer(List<LootItemCondition> conditions) {
|
||||
diff --git a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
|
||||
index d5e697a0cf6091a7f37c68e3c2a52851535735b1..a8a5a872a8647896e80f91cb5a89adead4005cf7 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
|
||||
@@ -25,7 +25,7 @@ public class NestedLootTable extends LootPoolSingletonContainer {
|
||||
.and(singletonFields(instance))
|
||||
.apply(instance, NestedLootTable::new)
|
||||
);
|
||||
- private final Either<ResourceKey<LootTable>, LootTable> contents;
|
||||
+ public final Either<ResourceKey<LootTable>, LootTable> contents; // Leaves - private -> public
|
||||
|
||||
private NestedLootTable(
|
||||
Either<ResourceKey<LootTable>, LootTable> contents, int weight, int quality, List<LootItemCondition> conditions, List<LootItemFunction> functions
|
||||
diff --git a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
|
||||
index 7134c54984a12949cd6a2e8dc35c2e1c0431e524..52f36fbb9bfcad81004e531efab85e9b87d3284d 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
|
||||
@@ -11,7 +11,7 @@ import net.minecraft.world.level.storage.loot.LootContext;
|
||||
import net.minecraft.world.level.storage.loot.ValidationContext;
|
||||
|
||||
public abstract class CompositeLootItemCondition implements LootItemCondition {
|
||||
- protected final List<LootItemCondition> terms;
|
||||
+ public final List<LootItemCondition> terms; // Leaves - private -> public
|
||||
private final Predicate<LootContext> composedPredicate;
|
||||
|
||||
protected CompositeLootItemCondition(List<LootItemCondition> terms, Predicate<LootContext> composedPredicate) {
|
||||
@@ -1,22 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Fri, 27 Jan 2023 09:42:57 +0800
|
||||
Subject: [PATCH] Leaves: Xaero Map Protocol
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
This patch is Powered by Xaero Map
|
||||
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 924faf76763588fb41b8aee53236ccb05b1239b1..e3d09d5f4efb32bb276e001e5ee747a775b502ee 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -1222,6 +1222,7 @@ public abstract class PlayerList {
|
||||
player.connection.send(new ClientboundInitializeBorderPacket(worldBorder));
|
||||
player.connection.send(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)));
|
||||
player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getSharedSpawnPos(), level.getSharedSpawnAngle()));
|
||||
+ org.leavesmc.leaves.protocol.XaeroMapProtocol.onSendWorldInfo(player); // Leaves - xaero map protocol
|
||||
if (level.isRaining()) {
|
||||
// CraftBukkit start - handle player weather
|
||||
// player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F));
|
||||
@@ -1,31 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Thu, 18 May 2023 16:16:56 +0800
|
||||
Subject: [PATCH] Leaves: Syncmatica Protocol
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
This patch is Powered by Syncmatica (https://github.com/End-Tech/syncmatica)
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 61bf3bfb41392d38c7e796f56cc0cce870e12631..543e7e278bb94c0bc996c377f8778df8a0b0440b 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -326,6 +326,7 @@ public class ServerGamePacketListenerImpl
|
||||
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile);
|
||||
this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat
|
||||
this.tickEndEvent = new io.papermc.paper.event.packet.ClientTickEndEvent(player.getBukkitEntity()); // Paper - add client tick end event
|
||||
+ this.exchangeTarget = new org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget(this); // Leaves - Syncmatica Protocol
|
||||
}
|
||||
|
||||
// Purpur start - AFK API
|
||||
@@ -342,6 +343,8 @@ public class ServerGamePacketListenerImpl
|
||||
);
|
||||
// Purpur end - AFK API
|
||||
|
||||
+ public final org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget exchangeTarget; // Leaves - Syncmatica Protocol
|
||||
+
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.ackBlockChangesUpTo > -1) {
|
||||
@@ -1,393 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Thu, 3 Aug 2023 20:36:38 +0800
|
||||
Subject: [PATCH] Leaves: Replay Mod API
|
||||
|
||||
Co-authored-by: alazeprt <nono135246@126.com>
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
This patch is Powered by ReplayMod(https://github.com/ReplayMod)
|
||||
|
||||
diff --git a/net/minecraft/commands/CommandSourceStack.java b/net/minecraft/commands/CommandSourceStack.java
|
||||
index 59c70c567051bc7dba0d308387352d1b15f3c842..e654387167cf3e9a88f0e62be940fe398387cffa 100644
|
||||
--- a/net/minecraft/commands/CommandSourceStack.java
|
||||
+++ b/net/minecraft/commands/CommandSourceStack.java
|
||||
@@ -626,7 +626,7 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
|
||||
|
||||
@Override
|
||||
public Collection<String> getOnlinePlayerNames() {
|
||||
- return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().getName()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players
|
||||
+ return this.entity instanceof ServerPlayer sourcePlayer && !(sourcePlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer) && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().getName()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players // Leaves - only real player
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/commands/arguments/selector/EntitySelector.java b/net/minecraft/commands/arguments/selector/EntitySelector.java
|
||||
index b305ba9bab617bf4e52d0e6ddf160bacc5751a94..c9b4f00d5ccde83898ecf69efdbfee7a3f91b96d 100644
|
||||
--- a/net/minecraft/commands/arguments/selector/EntitySelector.java
|
||||
+++ b/net/minecraft/commands/arguments/selector/EntitySelector.java
|
||||
@@ -128,11 +128,12 @@ public class EntitySelector {
|
||||
return this.findPlayers(source);
|
||||
} else if (this.playerName != null) {
|
||||
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName);
|
||||
+ playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer
|
||||
return playerByName == null ? List.of() : List.of(playerByName);
|
||||
} else if (this.entityUUID != null) {
|
||||
for (ServerLevel serverLevel : source.getServer().getAllLevels()) {
|
||||
Entity entity = serverLevel.getEntity(this.entityUUID);
|
||||
- if (entity != null) {
|
||||
+ if (entity != null && !(entity instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // Leaves - skip photographer
|
||||
if (entity.getType().isEnabled(source.enabledFeatures())) {
|
||||
return List.of(entity);
|
||||
}
|
||||
@@ -146,7 +147,7 @@ public class EntitySelector {
|
||||
AABB absoluteAabb = this.getAbsoluteAabb(vec3);
|
||||
if (this.currentEntity) {
|
||||
Predicate<Entity> predicate = this.getPredicate(vec3, absoluteAabb, null);
|
||||
- return source.getEntity() != null && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of();
|
||||
+ return source.getEntity() != null && !(source.getEntity() instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of(); // Leaves - skip photographer
|
||||
} else {
|
||||
Predicate<Entity> predicate = this.getPredicate(vec3, absoluteAabb, source.enabledFeatures());
|
||||
List<Entity> list = new ObjectArrayList<>();
|
||||
@@ -157,6 +158,7 @@ public class EntitySelector {
|
||||
this.addEntities(list, serverLevel1, absoluteAabb, predicate);
|
||||
}
|
||||
}
|
||||
+ list.removeIf(entity -> entity instanceof org.leavesmc.leaves.replay.ServerPhotographer); // Leaves - skip photographer
|
||||
|
||||
return this.sortAndLimit(vec3, list);
|
||||
}
|
||||
@@ -192,27 +194,30 @@ public class EntitySelector {
|
||||
this.checkPermissions(source);
|
||||
if (this.playerName != null) {
|
||||
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName);
|
||||
+ playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer
|
||||
return playerByName == null || !canSee(source, playerByName) ? List.of() : List.of(playerByName); // Purpur - Hide hidden players from entity selector
|
||||
} else if (this.entityUUID != null) {
|
||||
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayer(this.entityUUID);
|
||||
+ playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer
|
||||
return playerByName == null || !canSee(source, playerByName) ? List.of() : List.of(playerByName); // Purpur - Hide hidden players from entity selector
|
||||
} else {
|
||||
Vec3 vec3 = this.position.apply(source.getPosition());
|
||||
AABB absoluteAabb = this.getAbsoluteAabb(vec3);
|
||||
Predicate<Entity> predicate = this.getPredicate(vec3, absoluteAabb, null);
|
||||
if (this.currentEntity) {
|
||||
+ //return source.getEntity() instanceof ServerPlayer serverPlayer && predicate.test(serverPlayer) && !(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer) && canSee(source, serverPlayer) ? List.of(serverPlayer) : List.of(); // Purpur - Hide hidden players from entity selector // Leaves - skip photographer
|
||||
return source.getEntity() instanceof ServerPlayer serverPlayer && predicate.test(serverPlayer) && canSee(source, serverPlayer) ? List.of(serverPlayer) : List.of(); // Purpur - Hide hidden players from entity selector
|
||||
} else {
|
||||
int resultLimit = this.getResultLimit();
|
||||
List<ServerPlayer> players;
|
||||
if (this.isWorldLimited()) {
|
||||
players = source.getLevel().getPlayers(predicate, resultLimit);
|
||||
- players.removeIf(entityplayer3 -> !canSee(source, entityplayer3)); // Purpur - Hide hidden players from entity selector
|
||||
+ players.removeIf(entityplayer3 -> entityplayer3 instanceof org.leavesmc.leaves.replay.ServerPhotographer || !canSee(source, entityplayer3)); // Purpur - Hide hidden players from entity selector // Leaves - skip photographer
|
||||
} else {
|
||||
players = new ObjectArrayList<>();
|
||||
|
||||
for (ServerPlayer serverPlayer1 : source.getServer().getPlayerList().getPlayers()) {
|
||||
- if (predicate.test(serverPlayer1) && canSee(source, serverPlayer1)) { // Purpur - Hide hidden players from entity selector
|
||||
+ if (!(serverPlayer1 instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(serverPlayer1) && canSee(source, serverPlayer1)) { // Purpur - Hide hidden players from entity selector // Leaves - skip photographer
|
||||
players.add(serverPlayer1);
|
||||
if (players.size() >= resultLimit) {
|
||||
return players;
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index dddbb18992348fb7e8a6552423d134809cd7fdbc..0e6e71030e3fd1335fff796b861524a48cb0a507 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1650,7 +1650,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
|
||||
private ServerStatus.Players buildPlayerStatus() {
|
||||
- List<ServerPlayer> players = this.playerList.getPlayers();
|
||||
+ List<ServerPlayer> players = this.playerList.realPlayers; // Leaves - only real player
|
||||
int maxPlayers = this.getMaxPlayers();
|
||||
if (this.hidesOnlinePlayers()) {
|
||||
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
|
||||
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
|
||||
index abccabb8a0a1a9730b7df070dd25f3ca215af362..0a4bcc4c44fed2ededafaf0641315e072b7ba771 100644
|
||||
--- a/net/minecraft/server/PlayerAdvancements.java
|
||||
+++ b/net/minecraft/server/PlayerAdvancements.java
|
||||
@@ -168,6 +168,11 @@ public class PlayerAdvancements {
|
||||
}
|
||||
|
||||
public boolean award(AdvancementHolder advancement, String criterionKey) {
|
||||
+ // Leaves start - photographer can't get advancement
|
||||
+ if (player instanceof org.leavesmc.leaves.replay.ServerPhotographer) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Leaves end - photographer can't get advancement
|
||||
boolean flag = false;
|
||||
AdvancementProgress orStartProgress = this.getOrStartProgress(advancement);
|
||||
boolean isDone = orStartProgress.isDone();
|
||||
diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java
|
||||
index 5c0a04db38821dbb0cba2bb6f0787f113d167efd..cd153db93f709c3142942fac88ae3ca2226a65b3 100644
|
||||
--- a/net/minecraft/server/commands/OpCommand.java
|
||||
+++ b/net/minecraft/server/commands/OpCommand.java
|
||||
@@ -25,7 +25,7 @@ public class OpCommand {
|
||||
(context, builder) -> {
|
||||
PlayerList playerList = context.getSource().getServer().getPlayerList();
|
||||
return SharedSuggestionProvider.suggest(
|
||||
- playerList.getPlayers()
|
||||
+ playerList.realPlayers // Leaves - only real player
|
||||
.stream()
|
||||
.filter(player -> !playerList.isOp(player.getGameProfile()))
|
||||
.map(player -> player.getGameProfile().getName()),
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index b49dd636e730f0c5b609df68ee51bcd12efc1eaa..5e971bca365c692d4ce0c58693592002ce01471c 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -216,6 +216,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent
|
||||
private final alternate.current.wire.WireHandler wireHandler = new alternate.current.wire.WireHandler(this); // Paper - optimize redstone (Alternate Current)
|
||||
public boolean hasRidableMoveEvent = false; // Purpur - Ridables
|
||||
+ final List<ServerPlayer> realPlayers; // Leaves - skip
|
||||
|
||||
public LevelChunk getChunkIfLoaded(int x, int z) {
|
||||
return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately
|
||||
@@ -700,6 +701,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
// Paper end - rewrite chunk system
|
||||
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
|
||||
this.preciseTime = this.serverLevelData.getDayTime(); // Purpur - Configurable daylight cycle
|
||||
+ this.realPlayers = Lists.newArrayList(); // Leaves - skip
|
||||
}
|
||||
|
||||
// Paper start
|
||||
@@ -2698,6 +2700,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
// ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
|
||||
if (entity instanceof ServerPlayer serverPlayer) {
|
||||
ServerLevel.this.players.add(serverPlayer);
|
||||
+ // Leaves start - skip
|
||||
+ if (!(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer)) {
|
||||
+ ServerLevel.this.realPlayers.add(serverPlayer);
|
||||
+ }
|
||||
+ // Leaves end - skip
|
||||
ServerLevel.this.updateSleepingPlayerList();
|
||||
}
|
||||
|
||||
@@ -2768,6 +2775,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
ServerLevel.this.getChunkSource().removeEntity(entity);
|
||||
if (entity instanceof ServerPlayer serverPlayer) {
|
||||
ServerLevel.this.players.remove(serverPlayer);
|
||||
+ // Leaves start - skip
|
||||
+ if (!(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer)) {
|
||||
+ ServerLevel.this.realPlayers.remove(serverPlayer);
|
||||
+ }
|
||||
+ // Leaves end - skip
|
||||
ServerLevel.this.updateSleepingPlayerList();
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index d44c3baa2ef30d5cd4c46e491ff9198fa558513c..f89d28595fa9ca12e414f7b3cc86085ff0769e72 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -195,7 +195,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
);
|
||||
public ServerGamePacketListenerImpl connection;
|
||||
public final MinecraftServer server;
|
||||
- public final ServerPlayerGameMode gameMode;
|
||||
+ public ServerPlayerGameMode gameMode; // Leaves - final -> null
|
||||
private final PlayerAdvancements advancements;
|
||||
private final ServerStatsCounter stats;
|
||||
private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index c26bf04abe86b566e7f5cd29191a0a853f9808f8..4c172e2aee3e48d42009cd39b28f694aa71e20e3 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -132,6 +132,7 @@ public abstract class PlayerList {
|
||||
private boolean allowCommandsForAllPlayers;
|
||||
private static final boolean ALLOW_LOGOUTIVATOR = false;
|
||||
private int sendAllPlayerInfoIn;
|
||||
+ public final List<ServerPlayer> realPlayers = new java.util.concurrent.CopyOnWriteArrayList(); // Leaves - replay api
|
||||
|
||||
// CraftBukkit start
|
||||
private org.bukkit.craftbukkit.CraftServer cserver;
|
||||
@@ -150,6 +151,106 @@ public abstract class PlayerList {
|
||||
|
||||
abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor
|
||||
|
||||
+ // Leaves start - replay mod api
|
||||
+ public void placeNewPhotographer(Connection connection, org.leavesmc.leaves.replay.ServerPhotographer player, ServerLevel worldserver) {
|
||||
+ player.isRealPlayer = true; // Paper
|
||||
+ player.loginTime = System.currentTimeMillis(); // Paper
|
||||
+
|
||||
+ ServerLevel worldserver1 = worldserver;
|
||||
+
|
||||
+ player.setServerLevel(worldserver1);
|
||||
+ player.spawnIn(worldserver1);
|
||||
+ player.gameMode.setLevel((ServerLevel) player.level());
|
||||
+
|
||||
+ LevelData worlddata = worldserver1.getLevelData();
|
||||
+
|
||||
+ player.loadGameTypes(null);
|
||||
+ ServerGamePacketListenerImpl playerconnection = new ServerGamePacketListenerImpl(this.server, connection, player, CommonListenerCookie.createInitial(player.gameProfile, false));
|
||||
+ GameRules gamerules = worldserver1.getGameRules();
|
||||
+ boolean flag = gamerules.getBoolean(GameRules.RULE_DO_IMMEDIATE_RESPAWN);
|
||||
+ boolean flag1 = gamerules.getBoolean(GameRules.RULE_REDUCEDDEBUGINFO);
|
||||
+ boolean flag2 = gamerules.getBoolean(GameRules.RULE_LIMITED_CRAFTING);
|
||||
+
|
||||
+ playerconnection.send(new ClientboundLoginPacket(player.getId(), worlddata.isHardcore(), this.server.levelKeys(), this.getMaxPlayers(), worldserver1.getWorld().getSendViewDistance(), worldserver1.getWorld().getSimulationDistance(), flag1, !flag, flag2, player.createCommonSpawnInfo(worldserver1), this.server.enforceSecureProfile())); // Paper - replace old player chunk management
|
||||
+ player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit
|
||||
+ playerconnection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
|
||||
+ playerconnection.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities()));
|
||||
+ playerconnection.send(new ClientboundSetHeldSlotPacket(player.getInventory().selected));
|
||||
+ RecipeManager craftingmanager = this.server.getRecipeManager();
|
||||
+ playerconnection.send(new ClientboundUpdateRecipesPacket(craftingmanager.getSynchronizedItemProperties(), craftingmanager.getSynchronizedStonecutterRecipes()));
|
||||
+
|
||||
+ this.sendPlayerPermissionLevel(player);
|
||||
+ player.getStats().markAllDirty();
|
||||
+ player.getRecipeBook().sendInitialRecipeBook(player);
|
||||
+ this.updateEntireScoreboard(worldserver1.getScoreboard(), player);
|
||||
+ this.server.invalidateStatus();
|
||||
+
|
||||
+ playerconnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
|
||||
+ ServerStatus serverping = this.server.getStatus();
|
||||
+
|
||||
+ if (serverping != null) {
|
||||
+ player.sendServerStatus(serverping);
|
||||
+ }
|
||||
+
|
||||
+ this.players.add(player);
|
||||
+ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot
|
||||
+ this.playersByUUID.put(player.getUUID(), player);
|
||||
+
|
||||
+ player.supressTrackerForLogin = true;
|
||||
+ worldserver1.addNewPlayer(player);
|
||||
+ this.server.getCustomBossEvents().onPlayerConnect(player);
|
||||
+ org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity();
|
||||
+
|
||||
+ player.containerMenu.transferTo(player.containerMenu, bukkitPlayer);
|
||||
+ if (!player.connection.isAcceptingMessages()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol
|
||||
+
|
||||
+ final List<ServerPlayer> onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1);
|
||||
+ for (int i = 0; i < this.players.size(); ++i) {
|
||||
+ ServerPlayer entityplayer1 = this.players.get(i);
|
||||
+
|
||||
+ if (entityplayer1 == player || !bukkitPlayer.canSee(entityplayer1.getBukkitEntity())) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ onlinePlayers.add(entityplayer1);
|
||||
+ }
|
||||
+ if (!onlinePlayers.isEmpty()) {
|
||||
+ player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player));
|
||||
+ }
|
||||
+
|
||||
+ player.sentListPacket = true;
|
||||
+ player.supressTrackerForLogin = false;
|
||||
+ ((ServerLevel) player.level()).getChunkSource().chunkMap.addEntity(player);
|
||||
+
|
||||
+ this.sendLevelInfo(player, worldserver1);
|
||||
+
|
||||
+ if (player.level() == worldserver1 && !worldserver1.players().contains(player)) {
|
||||
+ worldserver1.addNewPlayer(player);
|
||||
+ this.server.getCustomBossEvents().onPlayerConnect(player);
|
||||
+ }
|
||||
+
|
||||
+ worldserver1 = player.serverLevel();
|
||||
+ java.util.Iterator<MobEffectInstance> iterator = player.getActiveEffects().iterator();
|
||||
+ while (iterator.hasNext()) {
|
||||
+ MobEffectInstance mobeffect = iterator.next();
|
||||
+ playerconnection.send(new ClientboundUpdateMobEffectPacket(player.getId(), mobeffect, false));
|
||||
+ }
|
||||
+
|
||||
+ if (player.isDeadOrDying()) {
|
||||
+ net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> plains = worldserver1.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME)
|
||||
+ .getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
|
||||
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket(
|
||||
+ new net.minecraft.world.level.chunk.EmptyLevelChunk(worldserver1, player.chunkPosition(), plains),
|
||||
+ worldserver1.getLightEngine(), null, null, false)
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaves end - replay mod api
|
||||
+
|
||||
public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie) {
|
||||
player.isRealPlayer = true; // Paper
|
||||
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
|
||||
@@ -315,6 +416,7 @@ public abstract class PlayerList {
|
||||
|
||||
// player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below
|
||||
this.players.add(player);
|
||||
+ this.realPlayers.add(player); // Leaves - replay api
|
||||
this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot
|
||||
this.playersByUUID.put(player.getUUID(), player);
|
||||
this.addToSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info
|
||||
@@ -374,6 +476,12 @@ public abstract class PlayerList {
|
||||
continue;
|
||||
}
|
||||
|
||||
+ // Leaves start - skip photographer
|
||||
+ if (entityplayer1 instanceof org.leavesmc.leaves.replay.ServerPhotographer) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Leaves end - skip photographer
|
||||
+
|
||||
onlinePlayers.add(entityplayer1); // Paper - Use single player info update packet on join
|
||||
}
|
||||
// Paper start - Use single player info update packet on join
|
||||
@@ -515,6 +623,43 @@ public abstract class PlayerList {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Leaves start - replay mod api
|
||||
+ public void removePhotographer(org.leavesmc.leaves.replay.ServerPhotographer entityplayer) {
|
||||
+ ServerLevel worldserver = entityplayer.serverLevel();
|
||||
+
|
||||
+ entityplayer.awardStat(Stats.LEAVE_GAME);
|
||||
+
|
||||
+ if (entityplayer.containerMenu != entityplayer.inventoryMenu) {
|
||||
+ entityplayer.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT);
|
||||
+ }
|
||||
+
|
||||
+ if (server.isSameThread()) entityplayer.doTick();
|
||||
+
|
||||
+ if (this.collideRuleTeamName != null) {
|
||||
+ final net.minecraft.world.scores.Scoreboard scoreBoard = this.server.getLevel(Level.OVERWORLD).getScoreboard();
|
||||
+ final PlayerTeam team = scoreBoard.getPlayersTeam(this.collideRuleTeamName);
|
||||
+ if (entityplayer.getTeam() == team && team != null) {
|
||||
+ scoreBoard.removePlayerFromTeam(entityplayer.getScoreboardName(), team);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
|
||||
+ entityplayer.retireScheduler();
|
||||
+ entityplayer.getAdvancements().stopListening();
|
||||
+ this.players.remove(entityplayer);
|
||||
+ this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT));
|
||||
+ this.server.getCustomBossEvents().onPlayerDisconnect(entityplayer);
|
||||
+ UUID uuid = entityplayer.getUUID();
|
||||
+ ServerPlayer entityplayer1 = this.playersByUUID.get(uuid);
|
||||
+
|
||||
+ if (entityplayer1 == entityplayer) {
|
||||
+ this.playersByUUID.remove(uuid);
|
||||
+ }
|
||||
+
|
||||
+ this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
|
||||
+ }
|
||||
+ // Leaves stop - replay mod api
|
||||
+
|
||||
public net.kyori.adventure.text.Component remove(ServerPlayer player) { // CraftBukkit - return string // Paper - return Component
|
||||
// Paper start - Fix kick event leave message not being sent
|
||||
return this.remove(player, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? player.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(player.getDisplayName())));
|
||||
@@ -591,6 +736,7 @@ public abstract class PlayerList {
|
||||
player.retireScheduler(); // Paper - Folia schedulers
|
||||
player.getAdvancements().stopListening();
|
||||
this.players.remove(player);
|
||||
+ this.realPlayers.remove(player); // Leaves - replay api
|
||||
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
|
||||
this.removeFromSendAllPlayerInfoBuckets(player); // Gale - Purpur - spread out sending all player info
|
||||
this.server.getCustomBossEvents().onPlayerDisconnect(player);
|
||||
@@ -688,7 +834,7 @@ public abstract class PlayerList {
|
||||
// return this.players.size() >= this.maxPlayers && !this.canBypassPlayerLimit(gameProfile)
|
||||
// ? Component.translatable("multiplayer.disconnect.server_full")
|
||||
// : null;
|
||||
- if (this.players.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameProfile))) { // Purpur - Allow player join full server by permission
|
||||
+ if (this.realPlayers.size() >= this.maxPlayers && !(player.hasPermission("purpur.joinfullserver") || this.canBypassPlayerLimit(gameProfile))) { // Purpur - Allow player join full server by permission // Leaves - only real player
|
||||
event.disallow(org.bukkit.event.player.PlayerLoginEvent.Result.KICK_FULL, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.serverFullMessage)); // Spigot // Paper - Adventure
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Tue, 26 Sep 2023 19:00:41 +0800
|
||||
Subject: [PATCH] Leaves: Protocol Core
|
||||
|
||||
TODO: Check whether Leaves's Return-nether-portal-fix.patch improves performance
|
||||
and change store way to sql maybe?
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
Commit: 41476d86922416c45f703df2871890831fc42bb5
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index ddf9a5d3af7ebe8e5c69d7dcb8a1c3a28d8178e3..3fabeaa91832eacc103416682aec3ce6121858fb 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -509,6 +509,7 @@ public final class CraftServer implements Server {
|
||||
this.potionBrewer = new io.papermc.paper.potion.PaperPotionBrewer(console); // Paper - custom potion mixes
|
||||
datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
|
||||
this.spark = new io.papermc.paper.SparksFly(this); // Paper - spark
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.init(); // Leaves - protocol
|
||||
}
|
||||
|
||||
public boolean getCommandBlockOverride(String command) {
|
||||
@@ -1141,6 +1142,7 @@ public final class CraftServer implements Server {
|
||||
org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur - Purpur config files
|
||||
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
||||
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleServerReload(); // Leaves - protocol
|
||||
|
||||
int pollCount = 0;
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Thu, 3 Aug 2023 20:36:38 +0800
|
||||
Subject: [PATCH] Leaves: Replay Mod API
|
||||
|
||||
Co-authored-by: alazeprt <nono135246@126.com>
|
||||
|
||||
Original license: GPLv3
|
||||
Original project: https://github.com/LeavesMC/Leaves
|
||||
|
||||
This patch is Powered by ReplayMod(https://github.com/ReplayMod)
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
index 4c003acccdd2dd17918b15316001e52e7670123e..780f3a48152fef6a06dc67bf7fbd1965b13bc4fa 100644
|
||||
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
|
||||
@@ -40,6 +40,11 @@ class PaperEventManager {
|
||||
} else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) {
|
||||
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
|
||||
}
|
||||
+ // Leaves start - skip photographer
|
||||
+ if (event instanceof org.bukkit.event.player.PlayerEvent playerEvent && playerEvent.getPlayer() instanceof org.leavesmc.leaves.entity.Photographer) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Leaves end - skip photographer
|
||||
|
||||
HandlerList handlers = event.getHandlers();
|
||||
RegisteredListener[] listeners = handlers.getRegisteredListeners();
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 3fabeaa91832eacc103416682aec3ce6121858fb..7ca77be262e6c8c9882db42295a42487b672d43e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -323,6 +323,8 @@ public final class CraftServer implements Server {
|
||||
private final io.papermc.paper.threadedregions.scheduler.FoliaAsyncScheduler asyncScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaAsyncScheduler();
|
||||
private final io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler globalRegionScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler();
|
||||
|
||||
+ private final org.leavesmc.leaves.entity.CraftPhotographerManager photographerManager = new org.leavesmc.leaves.entity.CraftPhotographerManager(); // Leaves
|
||||
+
|
||||
@Override
|
||||
public final io.papermc.paper.threadedregions.scheduler.RegionScheduler getRegionScheduler() {
|
||||
return this.regionizedScheduler;
|
||||
@@ -411,7 +413,7 @@ public final class CraftServer implements Server {
|
||||
public CraftServer(DedicatedServer console, PlayerList playerList) {
|
||||
this.console = console;
|
||||
this.playerList = (DedicatedPlayerList) playerList;
|
||||
- this.playerView = Collections.unmodifiableList(Lists.transform(playerList.players, new Function<ServerPlayer, CraftPlayer>() {
|
||||
+ this.playerView = Collections.unmodifiableList(Lists.transform(playerList.realPlayers, new Function<ServerPlayer, CraftPlayer>() { // Leaves - replay api
|
||||
@Override
|
||||
public CraftPlayer apply(ServerPlayer player) {
|
||||
return player.getBukkitEntity();
|
||||
@@ -3461,4 +3463,11 @@ public final class CraftServer implements Server {
|
||||
return getServer().lagging;
|
||||
}
|
||||
// Purpur end - Lagging threshold
|
||||
+
|
||||
+ // Leaves start - replay mod api
|
||||
+ @Override
|
||||
+ public org.leavesmc.leaves.entity.CraftPhotographerManager getPhotographerManager() {
|
||||
+ return photographerManager;
|
||||
+ }
|
||||
+ // Leaves end - replay mod api
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
index 8635cd772c5c2ae0ba326812ff2a1a179285a86f..cc024874fbde9678bdddfdca7c25080869d66de2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -117,6 +117,8 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
||||
return new CraftHumanEntity(server, (net.minecraft.world.entity.player.Player) entity);
|
||||
}
|
||||
|
||||
+ if (entity instanceof org.leavesmc.leaves.replay.ServerPhotographer photographer) { return new org.leavesmc.leaves.entity.CraftPhotographer(server, photographer); } // Leaves - replay mod api
|
||||
+
|
||||
// Special case complex part, since there is no extra entity type for them
|
||||
if (entity instanceof EnderDragonPart complexPart) {
|
||||
if (complexPart.parentMob instanceof EnderDragon) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index 9d733dd366d54b0780746a6235d81dc17607cad2..7bd58d683ca2534d36510590e0895dce7c46551a 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -2280,7 +2280,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
|
||||
@Override
|
||||
public boolean canSee(Player player) {
|
||||
- return this.canSee((org.bukkit.entity.Entity) player);
|
||||
+ return !(player instanceof org.leavesmc.leaves.entity.Photographer) && this.canSee((org.bukkit.entity.Entity) player); // Leaves - skip photographer
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.network;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollPackets;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollProtocol;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class ProtocolSupport extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.NETWORK.getBaseKeyName() + ".protocol-support";
|
||||
}
|
||||
|
||||
public static boolean jadeProtocol = false;
|
||||
public static boolean appleskinProtocol = false;
|
||||
public static int appleskinSyncTickInterval = 20;
|
||||
public static boolean asteorBarProtocol = false;
|
||||
public static boolean chatImageProtocol = false;
|
||||
public static boolean xaeroMapProtocol = false;
|
||||
public static int xaeroMapServerID = ThreadLocalRandom.current().nextInt(); // Leaf - Faster Random
|
||||
public static boolean syncmaticaProtocol = false;
|
||||
public static boolean syncmaticaQuota = false;
|
||||
public static int syncmaticaQuotaLimit = 40000000;
|
||||
|
||||
public static boolean doABarrelRollProtocol = false;
|
||||
public static boolean doABarrelRollAllowThrusting = false;
|
||||
public static boolean doABarrelRollForceEnabled = false;
|
||||
public static boolean doABarrelRollForceInstalled = false;
|
||||
public static int doABarrelRollInstalledTimeout = 40;
|
||||
public static DoABarrelRollPackets.KineticDamage doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.VANILLA;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
jadeProtocol = config.getBoolean(getBasePath() + ".jade-protocol", jadeProtocol);
|
||||
appleskinProtocol = config.getBoolean(getBasePath() + ".appleskin-protocol", appleskinProtocol);
|
||||
appleskinSyncTickInterval = config.getInt(getBasePath() + ".appleskin-protocol-sync-tick-interval", appleskinSyncTickInterval);
|
||||
asteorBarProtocol = config.getBoolean(getBasePath() + ".asteorbar-protocol", asteorBarProtocol);
|
||||
chatImageProtocol = config.getBoolean(getBasePath() + ".chatimage-protocol", chatImageProtocol);
|
||||
xaeroMapProtocol = config.getBoolean(getBasePath() + ".xaero-map-protocol", xaeroMapProtocol);
|
||||
xaeroMapServerID = config.getInt(getBasePath() + ".xaero-map-server-id", xaeroMapServerID);
|
||||
syncmaticaProtocol = config.getBoolean(getBasePath() + ".syncmatica-protocol", syncmaticaProtocol);
|
||||
syncmaticaQuota = config.getBoolean(getBasePath() + ".syncmatica-quota", syncmaticaQuota);
|
||||
syncmaticaQuotaLimit = config.getInt(getBasePath() + ".syncmatica-quota-limit", syncmaticaQuotaLimit);
|
||||
|
||||
if (syncmaticaProtocol) {
|
||||
org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol.init();
|
||||
}
|
||||
|
||||
doABarrelRollProtocol = config.getBoolean(getBasePath() + ".do-a-barrel-roll-protocol", doABarrelRollProtocol);
|
||||
doABarrelRollAllowThrusting = config.getBoolean(getBasePath() + ".do-a-barrel-roll-allow-thrusting", doABarrelRollAllowThrusting);
|
||||
doABarrelRollForceEnabled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-enabled", doABarrelRollForceEnabled);
|
||||
doABarrelRollForceInstalled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-installed", doABarrelRollForceInstalled);
|
||||
doABarrelRollInstalledTimeout = config.getInt(getBasePath() + ".do-a-barrel-roll-installed-timeout", 0);
|
||||
doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.valueOf(config.getString(getBasePath() + ".do-a-barrel-roll-kinetic-damage", doABarrelRollKineticDamage.name()));
|
||||
if (doABarrelRollInstalledTimeout <= 0) {
|
||||
doABarrelRollInstalledTimeout = 40;
|
||||
}
|
||||
if (doABarrelRollProtocol) {
|
||||
DoABarrelRollProtocol.init(
|
||||
doABarrelRollAllowThrusting,
|
||||
doABarrelRollForceEnabled,
|
||||
doABarrelRollForceInstalled,
|
||||
doABarrelRollInstalledTimeout,
|
||||
doABarrelRollKineticDamage
|
||||
);
|
||||
} else {
|
||||
DoABarrelRollProtocol.deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.leavesmc.leaves;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class LeavesLogger extends Logger {
|
||||
|
||||
public static final LeavesLogger LOGGER = new LeavesLogger();
|
||||
|
||||
private LeavesLogger() {
|
||||
super("Leaves", null);
|
||||
setParent(Bukkit.getLogger());
|
||||
setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
public void severe(String msg, Exception exception) {
|
||||
this.log(Level.SEVERE, msg, exception);
|
||||
}
|
||||
|
||||
public void warning(String msg, Exception exception) {
|
||||
this.log(Level.WARNING, msg, exception);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.stats.ServerStatsCounter;
|
||||
import net.minecraft.stats.Stat;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class BotStatsCounter extends ServerStatsCounter {
|
||||
|
||||
private static final File UNKOWN_FILE = new File("BOT_STATS_REMOVE_THIS");
|
||||
|
||||
public BotStatsCounter(MinecraftServer server) {
|
||||
super(server, UNKOWN_FILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(@NotNull Player player, @NotNull Stat<?> stat, int value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseLocal(@NotNull DataFixer dataFixer, @NotNull String json) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValue(@NotNull Stat<?> stat) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package org.leavesmc.leaves.entity;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.replay.ServerPhotographer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class CraftPhotographer extends CraftPlayer implements Photographer {
|
||||
|
||||
public CraftPhotographer(CraftServer server, ServerPhotographer entity) {
|
||||
super(server, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRecording() {
|
||||
this.stopRecording(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRecording(boolean async) {
|
||||
this.stopRecording(async, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRecording(boolean async, boolean save) {
|
||||
this.getHandle().remove(async, save);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseRecording() {
|
||||
this.getHandle().pauseRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeRecording() {
|
||||
this.getHandle().resumeRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordFile(@NotNull File file) {
|
||||
this.getHandle().setSaveFile(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFollowPlayer(@Nullable Player player) {
|
||||
ServerPlayer serverPlayer = player != null ? ((CraftPlayer) player).getHandle() : null;
|
||||
this.getHandle().setFollowPlayer(serverPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getId() {
|
||||
return this.getHandle().createState.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPhotographer getHandle() {
|
||||
return (ServerPhotographer) entity;
|
||||
}
|
||||
|
||||
public void setHandle(final ServerPhotographer entity) {
|
||||
super.setHandle(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CraftPhotographer{" + "name=" + getName() + '}';
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package org.leavesmc.leaves.entity;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.replay.BukkitRecorderOption;
|
||||
import org.leavesmc.leaves.replay.RecorderOption;
|
||||
import org.leavesmc.leaves.replay.ServerPhotographer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CraftPhotographerManager implements PhotographerManager {
|
||||
|
||||
private final Collection<Photographer> photographerViews = Collections.unmodifiableList(Lists.transform(ServerPhotographer.getPhotographers(), ServerPhotographer::getBukkitPlayer));
|
||||
|
||||
@Override
|
||||
public @Nullable Photographer getPhotographer(@NotNull UUID uuid) {
|
||||
ServerPhotographer photographer = ServerPhotographer.getPhotographer(uuid);
|
||||
if (photographer != null) {
|
||||
return photographer.getBukkitPlayer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Photographer getPhotographer(@NotNull String id) {
|
||||
ServerPhotographer photographer = ServerPhotographer.getPhotographer(id);
|
||||
if (photographer != null) {
|
||||
return photographer.getBukkitPlayer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Photographer createPhotographer(@NotNull String id, @NotNull Location location) {
|
||||
ServerPhotographer photographer = new ServerPhotographer.PhotographerCreateState(location, id, RecorderOption.createDefaultOption()).createSync();
|
||||
if (photographer != null) {
|
||||
return photographer.getBukkitPlayer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Photographer createPhotographer(@NotNull String id, @NotNull Location location, @NotNull BukkitRecorderOption recorderOption) {
|
||||
ServerPhotographer photographer = new ServerPhotographer.PhotographerCreateState(location, id, RecorderOption.createFromBukkit(recorderOption)).createSync();
|
||||
if (photographer != null) {
|
||||
return photographer.getBukkitPlayer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePhotographer(@NotNull String id) {
|
||||
ServerPhotographer photographer = ServerPhotographer.getPhotographer(id);
|
||||
if (photographer != null) {
|
||||
photographer.remove(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePhotographer(@NotNull UUID uuid) {
|
||||
ServerPhotographer photographer = ServerPhotographer.getPhotographer(uuid);
|
||||
if (photographer != null) {
|
||||
photographer.remove(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllPhotographers() {
|
||||
for (ServerPhotographer photographer : ServerPhotographer.getPhotographers()) {
|
||||
photographer.remove(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Photographer> getPhotographers() {
|
||||
return photographerViews;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.food.FoodData;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@LeavesProtocol(namespace = "appleskin")
|
||||
public class AppleSkinProtocol {
|
||||
|
||||
public static final String PROTOCOL_ID = "appleskin";
|
||||
|
||||
private static final ResourceLocation SATURATION_KEY = id("saturation");
|
||||
private static final ResourceLocation EXHAUSTION_KEY = id("exhaustion");
|
||||
private static final ResourceLocation NATURAL_REGENERATION_KEY = id("natural_regeneration");
|
||||
|
||||
private static final float MINIMUM_EXHAUSTION_CHANGE_THRESHOLD = 0.01F;
|
||||
|
||||
private static final Map<ServerPlayer, Float> previousSaturationLevels = new HashMap<>();
|
||||
private static final Map<ServerPlayer, Float> previousExhaustionLevels = new HashMap<>();
|
||||
private static final Map<ServerPlayer, Boolean> previousNaturalRegeneration = new HashMap<>();
|
||||
|
||||
private static final Map<ServerPlayer, Set<String>> subscribedChannels = new HashMap<>();
|
||||
|
||||
public static boolean shouldEnable() {
|
||||
return org.dreeam.leaf.config.modules.network.ProtocolSupport.appleskinProtocol;
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static ResourceLocation id(String path) {
|
||||
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerJoin
|
||||
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
|
||||
resetPlayerData(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
|
||||
subscribedChannels.remove(player);
|
||||
resetPlayerData(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.MinecraftRegister(ignoreId = true)
|
||||
public static void onPlayerSubscribed(@NotNull ServerPlayer player, String channel) {
|
||||
if (org.dreeam.leaf.config.modules.network.ProtocolSupport.appleskinProtocol) {
|
||||
subscribedChannels.computeIfAbsent(player, k -> new HashSet<>()).add(channel);
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker
|
||||
public static void tick() {
|
||||
if (MinecraftServer.getServer().getTickCount() % org.dreeam.leaf.config.modules.network.ProtocolSupport.appleskinSyncTickInterval != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<ServerPlayer, Set<String>> entry : subscribedChannels.entrySet()) {
|
||||
ServerPlayer player = entry.getKey();
|
||||
FoodData data = player.getFoodData();
|
||||
|
||||
for (String channel : entry.getValue()) {
|
||||
switch (channel) {
|
||||
case "saturation" -> {
|
||||
float saturation = data.getSaturationLevel();
|
||||
Float previousSaturation = previousSaturationLevels.get(player);
|
||||
if (previousSaturation == null || saturation != previousSaturation) {
|
||||
ProtocolUtils.sendPayloadPacket(player, SATURATION_KEY, buf -> buf.writeFloat(saturation));
|
||||
previousSaturationLevels.put(player, saturation);
|
||||
}
|
||||
}
|
||||
|
||||
case "exhaustion" -> {
|
||||
float exhaustion = data.exhaustionLevel;
|
||||
Float previousExhaustion = previousExhaustionLevels.get(player);
|
||||
if (previousExhaustion == null || Math.abs(exhaustion - previousExhaustion) >= MINIMUM_EXHAUSTION_CHANGE_THRESHOLD) {
|
||||
ProtocolUtils.sendPayloadPacket(player, EXHAUSTION_KEY, buf -> buf.writeFloat(exhaustion));
|
||||
previousExhaustionLevels.put(player, exhaustion);
|
||||
}
|
||||
}
|
||||
|
||||
case "natural_regeneration" -> {
|
||||
boolean regeneration = player.serverLevel().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION);
|
||||
Boolean previousRegeneration = previousNaturalRegeneration.get(player);
|
||||
if (previousRegeneration == null || regeneration != previousRegeneration) {
|
||||
ProtocolUtils.sendPayloadPacket(player, NATURAL_REGENERATION_KEY, buf -> buf.writeBoolean(regeneration));
|
||||
previousNaturalRegeneration.put(player, regeneration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadServer
|
||||
public static void onServerReload() {
|
||||
if (!org.dreeam.leaf.config.modules.network.ProtocolSupport.appleskinProtocol) {
|
||||
disableAllPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableAllPlayer() {
|
||||
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
|
||||
onPlayerLoggedOut(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetPlayerData(@NotNull ServerPlayer player) {
|
||||
previousExhaustionLevels.remove(player);
|
||||
previousSaturationLevels.remove(player);
|
||||
previousNaturalRegeneration.remove(player);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.food.FoodData;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@LeavesProtocol(namespace = "asteorbar")
|
||||
public class AsteorBarProtocol {
|
||||
|
||||
public static final String PROTOCOL_ID = "asteorbar";
|
||||
|
||||
private static final ResourceLocation NETWORK_KEY = id("network");
|
||||
|
||||
private static final Map<UUID, Float> previousSaturationLevels = new HashMap<>();
|
||||
private static final Map<UUID, Float> previousExhaustionLevels = new HashMap<>();
|
||||
|
||||
private static final float THRESHOLD = 0.01F;
|
||||
|
||||
private static final Set<ServerPlayer> players = new HashSet<>();
|
||||
|
||||
public static boolean shouldEnable() {
|
||||
return org.dreeam.leaf.config.modules.network.ProtocolSupport.asteorBarProtocol;
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ResourceLocation id(String path) {
|
||||
return ResourceLocation.fromNamespaceAndPath(PROTOCOL_ID, path);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerJoin
|
||||
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
|
||||
resetPlayerData(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
|
||||
players.remove(player);
|
||||
resetPlayerData(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.MinecraftRegister(ignoreId = true)
|
||||
public static void onPlayerSubscribed(@NotNull ServerPlayer player) {
|
||||
players.add(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker
|
||||
public static void tick() {
|
||||
for (ServerPlayer player : players) {
|
||||
FoodData data = player.getFoodData();
|
||||
|
||||
float saturation = data.getSaturationLevel();
|
||||
Float previousSaturation = previousSaturationLevels.get(player.getUUID());
|
||||
if (previousSaturation == null || saturation != previousSaturation) {
|
||||
ProtocolUtils.sendPayloadPacket(player, NETWORK_KEY, buf -> {
|
||||
buf.writeByte(1);
|
||||
buf.writeFloat(saturation);
|
||||
});
|
||||
previousSaturationLevels.put(player.getUUID(), saturation);
|
||||
}
|
||||
|
||||
float exhaustion = data.exhaustionLevel;
|
||||
Float previousExhaustion = previousExhaustionLevels.get(player.getUUID());
|
||||
if (previousExhaustion == null || Math.abs(exhaustion - previousExhaustion) >= THRESHOLD) {
|
||||
ProtocolUtils.sendPayloadPacket(player, NETWORK_KEY, buf -> {
|
||||
buf.writeByte(0);
|
||||
buf.writeFloat(exhaustion);
|
||||
});
|
||||
previousExhaustionLevels.put(player.getUUID(), exhaustion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadServer
|
||||
public static void onServerReload() {
|
||||
if (!org.dreeam.leaf.config.modules.network.ProtocolSupport.asteorBarProtocol) {
|
||||
disableAllPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableAllPlayer() {
|
||||
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
|
||||
onPlayerLoggedOut(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetPlayerData(@NotNull ServerPlayer player) {
|
||||
previousExhaustionLevels.remove(player.getUUID());
|
||||
previousSaturationLevels.remove(player.getUUID());
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
|
||||
@LeavesProtocol(namespace = {"xaerominimap", "xaeroworldmap"})
|
||||
public class XaeroMapProtocol {
|
||||
|
||||
public static final String PROTOCOL_ID_MINI = "xaerominimap";
|
||||
public static final String PROTOCOL_ID_WORLD = "xaeroworldmap";
|
||||
|
||||
private static final ResourceLocation MINIMAP_KEY = idMini("main");
|
||||
private static final ResourceLocation WORLDMAP_KEY = idWorld("main");
|
||||
|
||||
public static boolean shouldEnable() {
|
||||
return org.dreeam.leaf.config.modules.network.ProtocolSupport.xaeroMapProtocol;
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static ResourceLocation idMini(String path) {
|
||||
return ResourceLocation.tryBuild(PROTOCOL_ID_MINI, path);
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static ResourceLocation idWorld(String path) {
|
||||
return ResourceLocation.tryBuild(PROTOCOL_ID_WORLD, path);
|
||||
}
|
||||
|
||||
public static void onSendWorldInfo(@NotNull ServerPlayer player) {
|
||||
if (shouldEnable()) {
|
||||
ProtocolUtils.sendPayloadPacket(player, MINIMAP_KEY, buf -> {
|
||||
buf.writeByte(0);
|
||||
buf.writeInt(org.dreeam.leaf.config.modules.network.ProtocolSupport.xaeroMapServerID);
|
||||
});
|
||||
ProtocolUtils.sendPayloadPacket(player, WORLDMAP_KEY, buf -> {
|
||||
buf.writeByte(0);
|
||||
buf.writeInt(org.dreeam.leaf.config.modules.network.ProtocolSupport.xaeroMapServerID);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.chatimage;
|
||||
|
||||
public class ChatImageIndex {
|
||||
|
||||
public int index;
|
||||
public int total;
|
||||
public String url;
|
||||
public String bytes;
|
||||
|
||||
public ChatImageIndex(int index, int total, String url, String bytes) {
|
||||
this.index = index;
|
||||
this.total = total;
|
||||
this.url = url;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
public interface LeavesCustomPayload<T extends LeavesCustomPayload<T>> extends CustomPacketPayload {
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface New {
|
||||
}
|
||||
|
||||
void write(FriendlyByteBuf buf);
|
||||
|
||||
ResourceLocation id();
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
default Type<T> type() {
|
||||
return new CustomPacketPayload.Type<>(id());
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface LeavesProtocol {
|
||||
String[] namespace();
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.bukkit.event.player.PlayerKickEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class LeavesProtocolManager {
|
||||
|
||||
private static final Class<?>[] PAYLOAD_PARAMETER_TYPES = {ResourceLocation.class, FriendlyByteBuf.class};
|
||||
|
||||
private static final LeavesLogger LOGGER = LeavesLogger.LOGGER;
|
||||
|
||||
private static final Map<LeavesProtocol, Map<ProtocolHandler.PayloadReceiver, Executable>> KNOWN_TYPES = new HashMap<>();
|
||||
private static final Map<LeavesProtocol, Map<ProtocolHandler.PayloadReceiver, Method>> KNOW_RECEIVERS = new HashMap<>();
|
||||
private static Set<ResourceLocation> ALL_KNOWN_ID = new HashSet<>();
|
||||
|
||||
private static final List<Method> TICKERS = new ArrayList<>();
|
||||
private static final List<Method> PLAYER_JOIN = new ArrayList<>();
|
||||
private static final List<Method> PLAYER_LEAVE = new ArrayList<>();
|
||||
private static final List<Method> RELOAD_SERVER = new ArrayList<>();
|
||||
private static final Map<LeavesProtocol, Map<ProtocolHandler.MinecraftRegister, Method>> MINECRAFT_REGISTER = new HashMap<>();
|
||||
|
||||
public static void reload() {
|
||||
handleServerReload();
|
||||
cleanProtocols(); // Do cleanup
|
||||
init();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
boolean shouldEnable;
|
||||
|
||||
for (Class<?> clazz : org.dreeam.leaf.config.LeafConfig.getClasses("org.leavesmc.leaves.protocol")) {
|
||||
final LeavesProtocol protocol = clazz.getAnnotation(LeavesProtocol.class);
|
||||
if (protocol != null) {
|
||||
Set<Method> methods;
|
||||
try {
|
||||
Method[] publicMethods = clazz.getMethods();
|
||||
Method[] privateMethods = clazz.getDeclaredMethods();
|
||||
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0f);
|
||||
Collections.addAll(methods, publicMethods);
|
||||
Collections.addAll(methods, privateMethods);
|
||||
|
||||
Object instance = clazz.getConstructor().newInstance();
|
||||
Method method = clazz.getMethod("shouldEnable");
|
||||
shouldEnable = (boolean) method.invoke(instance);
|
||||
} catch (NoClassDefFoundError | InvocationTargetException | InstantiationException |
|
||||
IllegalAccessException | NoSuchMethodException error) {
|
||||
LOGGER.severe("Failed to load class " + clazz.getName() + " due to missing dependencies, " + error.getCause() + ": " + error.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
Map<ProtocolHandler.PayloadReceiver, Executable> map = KNOWN_TYPES.getOrDefault(protocol, new HashMap<>());
|
||||
for (final Method method : methods) {
|
||||
if (method.isBridge() || method.isSynthetic() || !Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
method.setAccessible(true);
|
||||
|
||||
final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class);
|
||||
if (reloadServer != null) {
|
||||
RELOAD_SERVER.add(method);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!shouldEnable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class);
|
||||
if (init != null) {
|
||||
try {
|
||||
method.invoke(null);
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.severe("Failed to invoke init method " + method.getName() + " in " + clazz.getName() + ", " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.PayloadReceiver receiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class);
|
||||
if (receiver != null) {
|
||||
try {
|
||||
boolean found = false;
|
||||
for (Method payloadMethod : receiver.payload().getDeclaredMethods()) {
|
||||
if (payloadMethod.isAnnotationPresent(LeavesCustomPayload.New.class)) {
|
||||
if (Arrays.equals(payloadMethod.getParameterTypes(), PAYLOAD_PARAMETER_TYPES) && payloadMethod.getReturnType() == receiver.payload() && Modifier.isStatic(payloadMethod.getModifiers())) {
|
||||
payloadMethod.setAccessible(true);
|
||||
map.put(receiver, payloadMethod);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Constructor<? extends LeavesCustomPayload<?>> constructor = receiver.payload().getConstructor(PAYLOAD_PARAMETER_TYPES);
|
||||
if (constructor.isAnnotationPresent(LeavesCustomPayload.New.class)) {
|
||||
constructor.setAccessible(true);
|
||||
map.put(receiver, constructor);
|
||||
} else {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException exception) {
|
||||
LOGGER.severe("Failed to find constructor for " + receiver.payload().getName() + ", " + exception.getCause() + ": " + exception.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!KNOW_RECEIVERS.containsKey(protocol)) {
|
||||
KNOW_RECEIVERS.put(protocol, new HashMap<>());
|
||||
}
|
||||
|
||||
KNOW_RECEIVERS.get(protocol).put(receiver, method);
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class);
|
||||
if (ticker != null) {
|
||||
TICKERS.add(method);
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class);
|
||||
if (playerJoin != null) {
|
||||
PLAYER_JOIN.add(method);
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class);
|
||||
if (playerLeave != null) {
|
||||
PLAYER_LEAVE.add(method);
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class);
|
||||
if (minecraftRegister != null) {
|
||||
if (!MINECRAFT_REGISTER.containsKey(protocol)) {
|
||||
MINECRAFT_REGISTER.put(protocol, new HashMap<>());
|
||||
}
|
||||
|
||||
MINECRAFT_REGISTER.get(protocol).put(minecraftRegister, method);
|
||||
}
|
||||
}
|
||||
KNOWN_TYPES.put(protocol, map);
|
||||
}
|
||||
}
|
||||
|
||||
for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) {
|
||||
Map<ProtocolHandler.PayloadReceiver, Executable> map = KNOWN_TYPES.get(protocol);
|
||||
for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) {
|
||||
if (receiver.sendFabricRegister() && !receiver.ignoreId()) {
|
||||
for (String payloadId : receiver.payloadId()) {
|
||||
for (String namespace : protocol.namespace()) {
|
||||
ALL_KNOWN_ID.add(ResourceLocation.tryBuild(namespace, payloadId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ALL_KNOWN_ID = ImmutableSet.copyOf(ALL_KNOWN_ID);
|
||||
}
|
||||
|
||||
private static void cleanProtocols() {
|
||||
KNOWN_TYPES.clear();
|
||||
KNOW_RECEIVERS.clear();
|
||||
//ALL_KNOWN_ID.clear(); // No need
|
||||
TICKERS.clear();
|
||||
PLAYER_JOIN.clear();
|
||||
PLAYER_LEAVE.clear();
|
||||
//RELOAD_SERVER.clear(); // No need
|
||||
MINECRAFT_REGISTER.clear();
|
||||
}
|
||||
|
||||
public static LeavesCustomPayload<?> decode(ResourceLocation id, FriendlyByteBuf buf) {
|
||||
for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) {
|
||||
if (!ArrayUtils.contains(protocol.namespace(), id.getNamespace())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<ProtocolHandler.PayloadReceiver, Executable> map = KNOWN_TYPES.get(protocol);
|
||||
for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) {
|
||||
if (receiver.ignoreId() || ArrayUtils.contains(receiver.payloadId(), id.getPath())) {
|
||||
try {
|
||||
if (map.get(receiver) instanceof Constructor<?> constructor) {
|
||||
return (LeavesCustomPayload<?>) constructor.newInstance(id, buf);
|
||||
} else if (map.get(receiver) instanceof Method method) {
|
||||
return (LeavesCustomPayload<?>) method.invoke(null, id, buf);
|
||||
}
|
||||
} catch (InvocationTargetException | InstantiationException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to create payload for " + id + " in " + ArrayUtils.toString(protocol.namespace()) + ", " + exception.getCause() + ": " + exception.getMessage());
|
||||
buf.readBytes(buf.readableBytes());
|
||||
return new ErrorPayload(id, protocol.namespace(), receiver.payloadId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void handlePayload(ServerPlayer player, LeavesCustomPayload<?> payload) {
|
||||
if (payload instanceof ErrorPayload errorPayload) {
|
||||
player.connection.disconnect(Component.literal("Payload " + Arrays.toString(errorPayload.packetID) + " from " + Arrays.toString(errorPayload.protocolID) + " error"), PlayerKickEvent.Cause.INVALID_PAYLOAD);
|
||||
return;
|
||||
}
|
||||
|
||||
for (LeavesProtocol protocol : KNOW_RECEIVERS.keySet()) {
|
||||
if (!ArrayUtils.contains(protocol.namespace(), payload.type().id().getNamespace())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<ProtocolHandler.PayloadReceiver, Method> map = KNOW_RECEIVERS.get(protocol);
|
||||
for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) {
|
||||
if (payload.getClass() == receiver.payload()) {
|
||||
if (receiver.ignoreId() || ArrayUtils.contains(receiver.payloadId(), payload.type().id().getPath())) {
|
||||
try {
|
||||
map.get(receiver).invoke(null, player, payload);
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to handle payload " + payload.type().id() + " in " + ArrayUtils.toString(protocol.namespace()) + ", " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleTick() {
|
||||
if (!TICKERS.isEmpty()) {
|
||||
try {
|
||||
for (Method method : TICKERS) {
|
||||
method.invoke(null);
|
||||
}
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to tick, " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handlePlayerJoin(ServerPlayer player) {
|
||||
if (!PLAYER_JOIN.isEmpty()) {
|
||||
try {
|
||||
for (Method method : PLAYER_JOIN) {
|
||||
method.invoke(null, player);
|
||||
}
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to handle player join, " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolUtils.sendPayloadPacket(player, new FabricRegisterPayload(ALL_KNOWN_ID));
|
||||
}
|
||||
|
||||
public static void handlePlayerLeave(ServerPlayer player) {
|
||||
if (!PLAYER_LEAVE.isEmpty()) {
|
||||
try {
|
||||
for (Method method : PLAYER_LEAVE) {
|
||||
method.invoke(null, player);
|
||||
}
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to handle player leave, " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleServerReload() {
|
||||
if (!RELOAD_SERVER.isEmpty()) {
|
||||
try {
|
||||
for (Method method : RELOAD_SERVER) {
|
||||
method.invoke(null);
|
||||
}
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to handle server reload, " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleMinecraftRegister(String channelId, ServerPlayer player) {
|
||||
for (LeavesProtocol protocol : MINECRAFT_REGISTER.keySet()) {
|
||||
String[] channel = channelId.split(":");
|
||||
if (!ArrayUtils.contains(protocol.namespace(), channel[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<ProtocolHandler.MinecraftRegister, Method> map = MINECRAFT_REGISTER.get(protocol);
|
||||
for (ProtocolHandler.MinecraftRegister register : map.keySet()) {
|
||||
if (register.ignoreId() || ArrayUtils.contains(register.channelId(), channel[1])) {
|
||||
try {
|
||||
map.get(register).invoke(null, player, channel[1]);
|
||||
} catch (InvocationTargetException | IllegalAccessException exception) {
|
||||
LOGGER.warning("Failed to handle minecraft register, " + exception.getCause() + ": " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record ErrorPayload(ResourceLocation id, String[] protocolID,
|
||||
String[] packetID) implements LeavesCustomPayload<ErrorPayload> {
|
||||
@Override
|
||||
public void write(@NotNull FriendlyByteBuf buf) {
|
||||
}
|
||||
}
|
||||
|
||||
public record EmptyPayload(ResourceLocation id) implements LeavesCustomPayload<EmptyPayload> {
|
||||
@New
|
||||
public EmptyPayload(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
this(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull FriendlyByteBuf buf) {
|
||||
}
|
||||
}
|
||||
|
||||
public record LeavesPayload(FriendlyByteBuf data,
|
||||
ResourceLocation id) implements LeavesCustomPayload<LeavesPayload> {
|
||||
@New
|
||||
public LeavesPayload(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
this(new FriendlyByteBuf(buf.readBytes(buf.readableBytes())), location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeBytes(data);
|
||||
}
|
||||
}
|
||||
|
||||
public record FabricRegisterPayload(
|
||||
Set<ResourceLocation> channels) implements LeavesCustomPayload<FabricRegisterPayload> {
|
||||
|
||||
public static final ResourceLocation CHANNEL = ResourceLocation.withDefaultNamespace("register");
|
||||
|
||||
@New
|
||||
public FabricRegisterPayload(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
this(buf.readCollection(HashSet::new, FriendlyByteBuf::readResourceLocation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
boolean first = true;
|
||||
|
||||
ResourceLocation channel;
|
||||
for (Iterator<ResourceLocation> var3 = this.channels.iterator(); var3.hasNext(); buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII))) {
|
||||
channel = var3.next();
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
buf.writeByte(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation id() {
|
||||
return CHANNEL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
public class ProtocolHandler {
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Init {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PayloadReceiver {
|
||||
Class<? extends LeavesCustomPayload<?>> payload();
|
||||
|
||||
String[] payloadId() default "";
|
||||
|
||||
boolean ignoreId() default false;
|
||||
|
||||
boolean sendFabricRegister() default true;
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Ticker {
|
||||
int delay() default 0;
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PlayerJoin {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PlayerLeave {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ReloadServer {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MinecraftRegister {
|
||||
|
||||
String[] channelId() default "";
|
||||
|
||||
boolean ignoreId() default false;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.papermc.paper.ServerBuildInfo;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ProtocolUtils {
|
||||
|
||||
private static final Function<ByteBuf, RegistryFriendlyByteBuf> bufDecorator = RegistryFriendlyByteBuf.decorator(MinecraftServer.getServer().registryAccess());
|
||||
|
||||
public static String buildProtocolVersion(String protocol) {
|
||||
return protocol + "-leaves-" + ServerBuildInfo.buildInfo().asString(ServerBuildInfo.StringRepresentation.VERSION_SIMPLE);
|
||||
}
|
||||
|
||||
public static void sendEmptyPayloadPacket(ServerPlayer player, ResourceLocation id) {
|
||||
player.connection.send(new ClientboundCustomPayloadPacket(new LeavesProtocolManager.EmptyPayload(id)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static void sendPayloadPacket(@NotNull ServerPlayer player, ResourceLocation id, Consumer<FriendlyByteBuf> consumer) {
|
||||
player.connection.send(new ClientboundCustomPayloadPacket(new LeavesCustomPayload() {
|
||||
@Override
|
||||
public void write(@NotNull FriendlyByteBuf buf) {
|
||||
consumer.accept(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ResourceLocation id() {
|
||||
return id;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static void sendPayloadPacket(ServerPlayer player, CustomPacketPayload payload) {
|
||||
player.connection.send(new ClientboundCustomPayloadPacket(payload));
|
||||
}
|
||||
|
||||
public static RegistryFriendlyByteBuf decorate(ByteBuf buf) {
|
||||
return bufDecorator.apply(buf);
|
||||
}
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.AgeableMob;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
import net.minecraft.world.entity.animal.Chicken;
|
||||
import net.minecraft.world.entity.animal.allay.Allay;
|
||||
import net.minecraft.world.entity.animal.armadillo.Armadillo;
|
||||
import net.minecraft.world.entity.animal.frog.Tadpole;
|
||||
import net.minecraft.world.entity.monster.ZombieVillager;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.CampfireBlock;
|
||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.CalibratedSculkSensorBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.CommandBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ComparatorBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.HopperBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.JukeboxBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.payload.ClientHandshakePayload;
|
||||
import org.leavesmc.leaves.protocol.jade.payload.ReceiveDataPayload;
|
||||
import org.leavesmc.leaves.protocol.jade.payload.RequestBlockPayload;
|
||||
import org.leavesmc.leaves.protocol.jade.payload.RequestEntityPayload;
|
||||
import org.leavesmc.leaves.protocol.jade.payload.ServerHandshakePayload;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerExtensionProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.ItemStorageExtensionProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.BeehiveProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.BrewingStandProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.CampfireProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.ChiseledBookshelfProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.CommandBlockProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.FurnaceProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.HopperLockProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.ItemStorageProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.JukeboxProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.LecternProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.MobSpawnerCooldownProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.ObjectNameProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.RedstoneProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.AnimalOwnerProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.MobBreedingProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.MobGrowthProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.NextEntityDropProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.PetArmorProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.StatusEffectsProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.ZombieVillagerProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.util.HierarchyLookup;
|
||||
import org.leavesmc.leaves.protocol.jade.util.LootTableMineableCollector;
|
||||
import org.leavesmc.leaves.protocol.jade.util.PairHierarchyLookup;
|
||||
import org.leavesmc.leaves.protocol.jade.util.PriorityStore;
|
||||
import org.leavesmc.leaves.protocol.jade.util.WrappedHierarchyLookup;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@LeavesProtocol(namespace = "jade")
|
||||
public class JadeProtocol {
|
||||
|
||||
public static PriorityStore<ResourceLocation, IJadeProvider> priorities;
|
||||
private static List<Block> shearableBlocks = null;
|
||||
|
||||
public static final String PROTOCOL_ID = "jade";
|
||||
public static final String PROTOCOL_VERSION = "7";
|
||||
|
||||
public static final HierarchyLookup<IServerDataProvider<EntityAccessor>> entityDataProviders = new HierarchyLookup<>(Entity.class);
|
||||
public static final PairHierarchyLookup<IServerDataProvider<BlockAccessor>> blockDataProviders = new PairHierarchyLookup<>(new HierarchyLookup<>(Block.class), new HierarchyLookup<>(BlockEntity.class));
|
||||
public static final WrappedHierarchyLookup<IServerExtensionProvider<ItemStack>> itemStorageProviders = WrappedHierarchyLookup.forAccessor();
|
||||
private static final Set<ServerPlayer> enabledPlayers = new HashSet<>();
|
||||
|
||||
public static boolean shouldEnable() {
|
||||
return org.dreeam.leaf.config.modules.network.ProtocolSupport.jadeProtocol;
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static ResourceLocation id(String path) {
|
||||
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ResourceLocation mc_id(String path) {
|
||||
return ResourceLocation.withDefaultNamespace(path);
|
||||
}
|
||||
|
||||
@ProtocolHandler.Init
|
||||
public static void init() {
|
||||
priorities = new PriorityStore<>(IJadeProvider::getDefaultPriority, IJadeProvider::getUid);
|
||||
|
||||
// core plugin
|
||||
blockDataProviders.register(BlockEntity.class, ObjectNameProvider.ForBlock.INSTANCE);
|
||||
|
||||
// universal plugin
|
||||
entityDataProviders.register(Entity.class, ItemStorageProvider.getEntity());
|
||||
blockDataProviders.register(Block.class, ItemStorageProvider.getBlock());
|
||||
|
||||
itemStorageProviders.register(Object.class, ItemStorageExtensionProvider.INSTANCE);
|
||||
itemStorageProviders.register(Block.class, ItemStorageExtensionProvider.INSTANCE);
|
||||
|
||||
// vanilla plugin
|
||||
entityDataProviders.register(Entity.class, AnimalOwnerProvider.INSTANCE);
|
||||
entityDataProviders.register(LivingEntity.class, StatusEffectsProvider.INSTANCE);
|
||||
entityDataProviders.register(AgeableMob.class, MobGrowthProvider.INSTANCE);
|
||||
entityDataProviders.register(Tadpole.class, MobGrowthProvider.INSTANCE);
|
||||
entityDataProviders.register(Animal.class, MobBreedingProvider.INSTANCE);
|
||||
entityDataProviders.register(Allay.class, MobBreedingProvider.INSTANCE);
|
||||
entityDataProviders.register(Mob.class, PetArmorProvider.INSTANCE);
|
||||
|
||||
entityDataProviders.register(Chicken.class, NextEntityDropProvider.INSTANCE);
|
||||
entityDataProviders.register(Armadillo.class, NextEntityDropProvider.INSTANCE);
|
||||
|
||||
entityDataProviders.register(ZombieVillager.class, ZombieVillagerProvider.INSTANCE);
|
||||
|
||||
blockDataProviders.register(BrewingStandBlockEntity.class, BrewingStandProvider.INSTANCE);
|
||||
blockDataProviders.register(BeehiveBlockEntity.class, BeehiveProvider.INSTANCE);
|
||||
blockDataProviders.register(CommandBlockEntity.class, CommandBlockProvider.INSTANCE);
|
||||
blockDataProviders.register(JukeboxBlockEntity.class, JukeboxProvider.INSTANCE);
|
||||
blockDataProviders.register(LecternBlockEntity.class, LecternProvider.INSTANCE);
|
||||
|
||||
blockDataProviders.register(ComparatorBlockEntity.class, RedstoneProvider.INSTANCE);
|
||||
blockDataProviders.register(HopperBlockEntity.class, HopperLockProvider.INSTANCE);
|
||||
blockDataProviders.register(CalibratedSculkSensorBlockEntity.class, RedstoneProvider.INSTANCE);
|
||||
|
||||
blockDataProviders.register(AbstractFurnaceBlockEntity.class, FurnaceProvider.INSTANCE);
|
||||
blockDataProviders.register(ChiseledBookShelfBlockEntity.class, ChiseledBookshelfProvider.INSTANCE);
|
||||
blockDataProviders.register(TrialSpawnerBlockEntity.class, MobSpawnerCooldownProvider.INSTANCE);
|
||||
|
||||
blockDataProviders.idMapped();
|
||||
entityDataProviders.idMapped();
|
||||
itemStorageProviders.register(CampfireBlock.class, CampfireProvider.INSTANCE);
|
||||
|
||||
blockDataProviders.loadComplete(priorities);
|
||||
entityDataProviders.loadComplete(priorities);
|
||||
itemStorageProviders.loadComplete(priorities);
|
||||
|
||||
rebuildShearableBlocks();
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = ClientHandshakePayload.class, payloadId = "client_handshake")
|
||||
public static void clientHandshake(ServerPlayer player, ClientHandshakePayload payload) {
|
||||
if (!payload.protocolVersion().equals(PROTOCOL_VERSION)) {
|
||||
player.sendSystemMessage(Component.literal("You are using a different version of Jade than the server. Please update Jade or report to the server operator").withColor(0xff0000));
|
||||
return;
|
||||
}
|
||||
ProtocolUtils.sendPayloadPacket(player, new ServerHandshakePayload(Collections.emptyMap(), shearableBlocks, blockDataProviders.mappedIds(), entityDataProviders.mappedIds()));
|
||||
enabledPlayers.add(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLeave(ServerPlayer player) {
|
||||
enabledPlayers.remove(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = RequestEntityPayload.class, payloadId = "request_entity")
|
||||
public static void requestEntityData(ServerPlayer player, RequestEntityPayload payload) {
|
||||
MinecraftServer.getServer().execute(() -> {
|
||||
EntityAccessor accessor = payload.data().unpack(player);
|
||||
if (accessor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Entity entity = accessor.getEntity();
|
||||
double maxDistance = Mth.square(player.entityInteractionRange() + 21);
|
||||
if (entity == null || player.distanceToSqr(entity) > maxDistance) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<IServerDataProvider<EntityAccessor>> providers = entityDataProviders.get(entity);
|
||||
if (providers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag tag = new CompoundTag();
|
||||
for (IServerDataProvider<EntityAccessor> provider : providers) {
|
||||
if (!payload.dataProviders().contains(provider)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
provider.appendServerData(tag, accessor);
|
||||
} catch (Exception e) {
|
||||
LeavesLogger.LOGGER.warning("Error while saving data for entity " + entity);
|
||||
}
|
||||
}
|
||||
tag.putInt("EntityId", entity.getId());
|
||||
|
||||
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
|
||||
});
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = RequestBlockPayload.class, payloadId = "request_block")
|
||||
public static void requestBlockData(ServerPlayer player, RequestBlockPayload payload) {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
server.execute(() -> {
|
||||
BlockAccessor accessor = payload.data().unpack(player);
|
||||
if (accessor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos pos = accessor.getPosition();
|
||||
Block block = accessor.getBlock();
|
||||
BlockEntity blockEntity = accessor.getBlockEntity();
|
||||
double maxDistance = Mth.square(player.blockInteractionRange() + 21);
|
||||
if (pos.distSqr(player.blockPosition()) > maxDistance || !accessor.getLevel().isLoaded(pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<IServerDataProvider<BlockAccessor>> providers;
|
||||
if (blockEntity != null) {
|
||||
providers = blockDataProviders.getMerged(block, blockEntity);
|
||||
} else {
|
||||
providers = blockDataProviders.first.get(block);
|
||||
}
|
||||
|
||||
if (providers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag tag = new CompoundTag();
|
||||
for (IServerDataProvider<BlockAccessor> provider : providers) {
|
||||
if (!payload.dataProviders().contains(provider)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
provider.appendServerData(tag, accessor);
|
||||
} catch (Exception e) {
|
||||
LeavesLogger.LOGGER.warning("Error while saving data for block " + accessor.getBlockState());
|
||||
}
|
||||
}
|
||||
tag.putInt("x", pos.getX());
|
||||
tag.putInt("y", pos.getY());
|
||||
tag.putInt("z", pos.getZ());
|
||||
tag.putString("BlockId", BuiltInRegistries.BLOCK.getKey(block).toString());
|
||||
|
||||
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
|
||||
});
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadServer
|
||||
public static void onServerReload() {
|
||||
if (org.dreeam.leaf.config.modules.network.ProtocolSupport.jadeProtocol) {
|
||||
rebuildShearableBlocks();
|
||||
for (ServerPlayer player : enabledPlayers) {
|
||||
ProtocolUtils.sendPayloadPacket(player, new ServerHandshakePayload(Collections.emptyMap(), shearableBlocks, blockDataProviders.mappedIds(), entityDataProviders.mappedIds()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void rebuildShearableBlocks() {
|
||||
try {
|
||||
shearableBlocks = Collections.unmodifiableList(LootTableMineableCollector.execute(
|
||||
MinecraftServer.getServer().reloadableRegistries().lookup().lookupOrThrow(Registries.LOOT_TABLE),
|
||||
Items.SHEARS.getDefaultInstance()
|
||||
));
|
||||
} catch (Throwable ignore) {
|
||||
shearableBlocks = List.of();
|
||||
LeavesLogger.LOGGER.severe("Failed to collect shearable blocks");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamEncoder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Accessor<T extends HitResult> {
|
||||
|
||||
Level getLevel();
|
||||
|
||||
Player getPlayer();
|
||||
|
||||
<D> Tag encodeAsNbt(StreamEncoder<RegistryFriendlyByteBuf, D> codec, D value);
|
||||
|
||||
T getHitResult();
|
||||
|
||||
/**
|
||||
* @return {@code true} if the dedicated server has Jade installed.
|
||||
*/
|
||||
boolean isServerConnected();
|
||||
|
||||
boolean showDetails();
|
||||
|
||||
@Nullable
|
||||
Object getTarget();
|
||||
|
||||
float tickRate();
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.nbt.ByteArrayTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamEncoder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class AccessorImpl<T extends HitResult> implements Accessor<T> {
|
||||
|
||||
private final Level level;
|
||||
private final Player player;
|
||||
private final Supplier<T> hit;
|
||||
private final boolean serverConnected;
|
||||
private final boolean showDetails;
|
||||
protected boolean verify;
|
||||
private RegistryFriendlyByteBuf buffer;
|
||||
|
||||
public AccessorImpl(Level level, Player player, Supplier<T> hit, boolean serverConnected, boolean showDetails) {
|
||||
this.level = level;
|
||||
this.player = player;
|
||||
this.hit = hit;
|
||||
this.serverConnected = serverConnected;
|
||||
this.showDetails = showDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
private RegistryFriendlyByteBuf buffer() {
|
||||
if (buffer == null) {
|
||||
buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), level.registryAccess());
|
||||
}
|
||||
buffer.clear();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <D> Tag encodeAsNbt(StreamEncoder<RegistryFriendlyByteBuf, D> streamCodec, D value) {
|
||||
RegistryFriendlyByteBuf buffer = buffer();
|
||||
streamCodec.encode(buffer, value);
|
||||
ByteArrayTag tag = new ByteArrayTag(ArrayUtils.subarray(buffer.array(), 0, buffer.readableBytes()));
|
||||
buffer.clear();
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getHitResult() {
|
||||
return hit.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if dedicated server has Jade installed.
|
||||
*/
|
||||
@Override
|
||||
public boolean isServerConnected() {
|
||||
return serverConnected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showDetails() {
|
||||
return showDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float tickRate() {
|
||||
return getLevel().tickRateManager().tickrate();
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface BlockAccessor extends Accessor<BlockHitResult> {
|
||||
|
||||
Block getBlock();
|
||||
|
||||
BlockState getBlockState();
|
||||
|
||||
BlockEntity getBlockEntity();
|
||||
|
||||
BlockPos getPosition();
|
||||
|
||||
Direction getSide();
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
interface Builder {
|
||||
Builder level(Level level);
|
||||
|
||||
Builder player(Player player);
|
||||
|
||||
Builder showDetails(boolean showDetails);
|
||||
|
||||
Builder hit(BlockHitResult hit);
|
||||
|
||||
Builder blockState(BlockState state);
|
||||
|
||||
default Builder blockEntity(BlockEntity blockEntity) {
|
||||
return blockEntity(() -> blockEntity);
|
||||
}
|
||||
|
||||
Builder blockEntity(Supplier<BlockEntity> blockEntity);
|
||||
|
||||
Builder from(BlockAccessor accessor);
|
||||
|
||||
BlockAccessor build();
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
|
||||
/**
|
||||
* Class to get information of block target and context.
|
||||
*/
|
||||
public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements BlockAccessor {
|
||||
|
||||
private final BlockState blockState;
|
||||
@Nullable
|
||||
private final Supplier<BlockEntity> blockEntity;
|
||||
|
||||
private BlockAccessorImpl(Builder builder) {
|
||||
super(builder.level, builder.player, Suppliers.ofInstance(builder.hit), builder.connected, builder.showDetails);
|
||||
blockState = builder.blockState;
|
||||
blockEntity = builder.blockEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock() {
|
||||
return getBlockState().getBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState() {
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity getBlockEntity() {
|
||||
return blockEntity == null ? null : blockEntity.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition() {
|
||||
return getHitResult().getBlockPos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction getSide() {
|
||||
return getHitResult().getDirection();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return getBlockEntity();
|
||||
}
|
||||
|
||||
public static class Builder implements BlockAccessor.Builder {
|
||||
|
||||
private Level level;
|
||||
private Player player;
|
||||
private boolean connected;
|
||||
private boolean showDetails;
|
||||
private BlockHitResult hit;
|
||||
private BlockState blockState = Blocks.AIR.defaultBlockState();
|
||||
private Supplier<BlockEntity> blockEntity;
|
||||
|
||||
@Override
|
||||
public Builder level(Level level) {
|
||||
this.level = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder player(Player player) {
|
||||
this.player = player;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder showDetails(boolean showDetails) {
|
||||
this.showDetails = showDetails;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder hit(BlockHitResult hit) {
|
||||
this.hit = hit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder blockState(BlockState blockState) {
|
||||
this.blockState = blockState;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder blockEntity(Supplier<BlockEntity> blockEntity) {
|
||||
this.blockEntity = blockEntity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder from(BlockAccessor accessor) {
|
||||
level = accessor.getLevel();
|
||||
player = accessor.getPlayer();
|
||||
connected = accessor.isServerConnected();
|
||||
showDetails = accessor.showDetails();
|
||||
hit = accessor.getHitResult();
|
||||
blockEntity = accessor::getBlockEntity;
|
||||
blockState = accessor.getBlockState();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockAccessor build() {
|
||||
return new BlockAccessorImpl(this);
|
||||
}
|
||||
}
|
||||
|
||||
public record SyncData(boolean showDetails, BlockHitResult hit, BlockState blockState, ItemStack fakeBlock) {
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, SyncData> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL,
|
||||
SyncData::showDetails,
|
||||
StreamCodec.of(FriendlyByteBuf::writeBlockHitResult, FriendlyByteBuf::readBlockHitResult),
|
||||
SyncData::hit,
|
||||
ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY),
|
||||
SyncData::blockState,
|
||||
ItemStack.OPTIONAL_STREAM_CODEC,
|
||||
SyncData::fakeBlock,
|
||||
SyncData::new
|
||||
);
|
||||
|
||||
public BlockAccessor unpack(ServerPlayer player) {
|
||||
Supplier<BlockEntity> blockEntity = null;
|
||||
if (blockState.hasBlockEntity()) {
|
||||
blockEntity = Suppliers.memoize(() -> player.level().getBlockEntity(hit.getBlockPos()));
|
||||
}
|
||||
return new Builder()
|
||||
.level(player.level())
|
||||
.player(player)
|
||||
.showDetails(showDetails)
|
||||
.hit(hit)
|
||||
.blockState(blockState)
|
||||
.blockEntity(blockEntity)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface EntityAccessor extends Accessor<EntityHitResult> {
|
||||
|
||||
Entity getEntity();
|
||||
|
||||
/**
|
||||
* For part entity like ender dragon's, getEntity() will return the parent entity.
|
||||
*/
|
||||
Entity getRawEntity();
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
interface Builder {
|
||||
Builder level(Level level);
|
||||
|
||||
Builder player(Player player);
|
||||
|
||||
Builder showDetails(boolean showDetails);
|
||||
|
||||
default Builder hit(EntityHitResult hit) {
|
||||
return hit(() -> hit);
|
||||
}
|
||||
|
||||
Builder hit(Supplier<EntityHitResult> hit);
|
||||
|
||||
default Builder entity(Entity entity) {
|
||||
return entity(() -> entity);
|
||||
}
|
||||
|
||||
Builder entity(Supplier<Entity> entity);
|
||||
|
||||
Builder from(EntityAccessor accessor);
|
||||
|
||||
EntityAccessor build();
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.util.CommonUtil;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements EntityAccessor {
|
||||
|
||||
private final Supplier<Entity> entity;
|
||||
|
||||
public EntityAccessorImpl(Builder builder) {
|
||||
super(builder.level, builder.player, builder.hit, builder.connected, builder.showDetails);
|
||||
entity = builder.entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity() {
|
||||
return CommonUtil.wrapPartEntityParent(getRawEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getRawEntity() {
|
||||
return entity.get();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return getEntity();
|
||||
}
|
||||
|
||||
public static class Builder implements EntityAccessor.Builder {
|
||||
|
||||
public boolean showDetails;
|
||||
private Level level;
|
||||
private Player player;
|
||||
private boolean connected;
|
||||
private Supplier<EntityHitResult> hit;
|
||||
private Supplier<Entity> entity;
|
||||
|
||||
@Override
|
||||
public Builder level(Level level) {
|
||||
this.level = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder player(Player player) {
|
||||
this.player = player;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder showDetails(boolean showDetails) {
|
||||
this.showDetails = showDetails;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder hit(Supplier<EntityHitResult> hit) {
|
||||
this.hit = hit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder entity(Supplier<Entity> entity) {
|
||||
this.entity = entity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder from(EntityAccessor accessor) {
|
||||
level = accessor.getLevel();
|
||||
player = accessor.getPlayer();
|
||||
connected = accessor.isServerConnected();
|
||||
showDetails = accessor.showDetails();
|
||||
hit = accessor::getHitResult;
|
||||
entity = accessor::getEntity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityAccessor build() {
|
||||
return new EntityAccessorImpl(this);
|
||||
}
|
||||
}
|
||||
|
||||
public record SyncData(boolean showDetails, int id, int partIndex, Vec3 hitVec) {
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, SyncData> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL,
|
||||
SyncData::showDetails,
|
||||
ByteBufCodecs.VAR_INT,
|
||||
SyncData::id,
|
||||
ByteBufCodecs.VAR_INT,
|
||||
SyncData::partIndex,
|
||||
ByteBufCodecs.VECTOR3F.map(Vec3::new, Vec3::toVector3f),
|
||||
SyncData::hitVec,
|
||||
SyncData::new
|
||||
);
|
||||
|
||||
public EntityAccessor unpack(ServerPlayer player) {
|
||||
Supplier<Entity> entity = Suppliers.memoize(() -> CommonUtil.getPartEntity(player.level().getEntity(id), partIndex));
|
||||
return new EntityAccessorImpl.Builder()
|
||||
.level(player.level())
|
||||
.player(player)
|
||||
.showDetails(showDetails)
|
||||
.entity(entity)
|
||||
.hit(Suppliers.memoize(() -> new EntityHitResult(entity.get(), hitVec)))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
|
||||
public record ClientHandshakePayload(String protocolVersion) implements LeavesCustomPayload<ClientHandshakePayload> {
|
||||
|
||||
private static final ResourceLocation PACKET_CLIENT_HANDSHAKE = JadeProtocol.id("client_handshake");
|
||||
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, ClientHandshakePayload> CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.STRING_UTF8,
|
||||
ClientHandshakePayload::protocolVersion,
|
||||
ClientHandshakePayload::new);
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
CODEC.encode(ProtocolUtils.decorate(buf), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation id() {
|
||||
return PACKET_CLIENT_HANDSHAKE;
|
||||
}
|
||||
|
||||
@New
|
||||
public static ClientHandshakePayload create(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
return CODEC.decode(ProtocolUtils.decorate(buf));
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
|
||||
public record ReceiveDataPayload(CompoundTag tag) implements LeavesCustomPayload<ReceiveDataPayload> {
|
||||
|
||||
private static final ResourceLocation PACKET_RECEIVE_DATA = JadeProtocol.id("receive_data");
|
||||
|
||||
@New
|
||||
public ReceiveDataPayload(ResourceLocation id, FriendlyByteBuf buf) {
|
||||
this(buf.readNbt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull FriendlyByteBuf buf) {
|
||||
buf.writeNbt(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation id() {
|
||||
return PACKET_RECEIVE_DATA;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessorImpl;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.leavesmc.leaves.protocol.jade.JadeProtocol.blockDataProviders;
|
||||
|
||||
public record RequestBlockPayload(BlockAccessorImpl.SyncData data, List<@Nullable IServerDataProvider<BlockAccessor>> dataProviders) implements LeavesCustomPayload<RequestBlockPayload> {
|
||||
|
||||
private static final ResourceLocation PACKET_REQUEST_BLOCK = JadeProtocol.id("request_block");
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, RequestBlockPayload> CODEC = StreamCodec.composite(
|
||||
BlockAccessorImpl.SyncData.STREAM_CODEC,
|
||||
RequestBlockPayload::data,
|
||||
ByteBufCodecs.<ByteBuf, IServerDataProvider<BlockAccessor>>list()
|
||||
.apply(ByteBufCodecs.idMapper(
|
||||
$ -> Objects.requireNonNull(blockDataProviders.idMapper()).byId($),
|
||||
$ -> Objects.requireNonNull(blockDataProviders.idMapper()).getIdOrThrow($))),
|
||||
RequestBlockPayload::dataProviders,
|
||||
RequestBlockPayload::new);
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
CODEC.encode(ProtocolUtils.decorate(buf), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ResourceLocation id() {
|
||||
return PACKET_REQUEST_BLOCK;
|
||||
}
|
||||
|
||||
@New
|
||||
public static RequestBlockPayload create(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
return CODEC.decode(ProtocolUtils.decorate(buf));
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessorImpl;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.leavesmc.leaves.protocol.jade.JadeProtocol.entityDataProviders;
|
||||
|
||||
public record RequestEntityPayload(EntityAccessorImpl.SyncData data,
|
||||
List<@Nullable IServerDataProvider<EntityAccessor>> dataProviders) implements LeavesCustomPayload<RequestEntityPayload> {
|
||||
|
||||
private static final ResourceLocation PACKET_REQUEST_ENTITY = JadeProtocol.id("request_entity");
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, RequestEntityPayload> CODEC = StreamCodec.composite(
|
||||
EntityAccessorImpl.SyncData.STREAM_CODEC,
|
||||
RequestEntityPayload::data,
|
||||
ByteBufCodecs.<ByteBuf, IServerDataProvider<EntityAccessor>>list()
|
||||
.apply(ByteBufCodecs.idMapper(
|
||||
$ -> Objects.requireNonNull(entityDataProviders.idMapper()).byId($),
|
||||
$ -> Objects.requireNonNull(entityDataProviders.idMapper()).getIdOrThrow($)
|
||||
)),
|
||||
RequestEntityPayload::dataProviders,
|
||||
RequestEntityPayload::new);
|
||||
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
CODEC.encode(ProtocolUtils.decorate(buf), this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ResourceLocation id() {
|
||||
return PACKET_REQUEST_ENTITY;
|
||||
}
|
||||
|
||||
@New
|
||||
public static RequestEntityPayload create(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
return CODEC.decode(ProtocolUtils.decorate(buf));
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.leavesmc.leaves.protocol.jade.util.JadeCodec.PRIMITIVE_STREAM_CODEC;
|
||||
|
||||
public record ServerHandshakePayload(Map<ResourceLocation, Object> serverConfig, List<Block> shearableBlocks,
|
||||
List<ResourceLocation> blockProviderIds,
|
||||
List<ResourceLocation> entityProviderIds) implements LeavesCustomPayload<ServerHandshakePayload> {
|
||||
|
||||
private static final ResourceLocation PACKET_SERVER_HANDSHAKE = JadeProtocol.id("server_handshake");
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, ServerHandshakePayload> CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.map(Maps::newHashMapWithExpectedSize, ResourceLocation.STREAM_CODEC, PRIMITIVE_STREAM_CODEC),
|
||||
ServerHandshakePayload::serverConfig,
|
||||
ByteBufCodecs.registry(Registries.BLOCK).apply(ByteBufCodecs.list()),
|
||||
ServerHandshakePayload::shearableBlocks,
|
||||
ByteBufCodecs.<ByteBuf, ResourceLocation>list().apply(ResourceLocation.STREAM_CODEC),
|
||||
ServerHandshakePayload::blockProviderIds,
|
||||
ByteBufCodecs.<ByteBuf, ResourceLocation>list().apply(ResourceLocation.STREAM_CODEC),
|
||||
ServerHandshakePayload::entityProviderIds,
|
||||
ServerHandshakePayload::new);
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
CODEC.encode(ProtocolUtils.decorate(buf), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation id() {
|
||||
return PACKET_SERVER_HANDSHAKE;
|
||||
}
|
||||
|
||||
@New
|
||||
public static ServerHandshakePayload create(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
return CODEC.decode(ProtocolUtils.decorate(buf));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface IJadeProvider {
|
||||
|
||||
ResourceLocation getUid();
|
||||
|
||||
default int getDefaultPriority() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
|
||||
public interface IServerDataProvider<T extends Accessor<?>> extends IJadeProvider {
|
||||
void appendServerData(CompoundTag data, T accessor);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IServerExtensionProvider<T> extends IJadeProvider {
|
||||
List<ViewGroup<T>> getGroups(Accessor<?> request);
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.LockCode;
|
||||
import net.minecraft.world.RandomizableContainer;
|
||||
import net.minecraft.world.WorldlyContainerHolder;
|
||||
import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.vehicle.ContainerEntity;
|
||||
import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ItemCollector;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ItemIterator;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public enum ItemStorageExtensionProvider implements IServerExtensionProvider<ItemStack> {
|
||||
INSTANCE;
|
||||
|
||||
public static final Cache<Object, ItemCollector<?>> targetCache = CacheBuilder.newBuilder().weakKeys().expireAfterAccess(60, TimeUnit.SECONDS).build();
|
||||
|
||||
private static final ResourceLocation UNIVERSAL_ITEM_STORAGE = JadeProtocol.mc_id("item_storage.default");
|
||||
|
||||
@Override
|
||||
public List<ViewGroup<ItemStack>> getGroups(Accessor<?> request) {
|
||||
Object target = request.getTarget();
|
||||
|
||||
switch (target) {
|
||||
case null -> {
|
||||
return createItemCollector(request).update(request);
|
||||
}
|
||||
case RandomizableContainer te when te.getLootTable() != null -> {
|
||||
return List.of();
|
||||
}
|
||||
case ContainerEntity containerEntity when containerEntity.getContainerLootTable() != null -> {
|
||||
return List.of();
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
Player player = request.getPlayer();
|
||||
if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
|
||||
if (te.lockKey != LockCode.NO_LOCK) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
if (target instanceof EnderChestBlockEntity) {
|
||||
PlayerEnderChestContainer inventory = player.getEnderChestInventory();
|
||||
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(x -> inventory, 0)).update(request);
|
||||
}
|
||||
|
||||
ItemCollector<?> itemCollector;
|
||||
try {
|
||||
itemCollector = targetCache.get(target, () -> createItemCollector(request));
|
||||
} catch (ExecutionException e) {
|
||||
LeavesLogger.LOGGER.severe("Failed to get item collector for " + target);
|
||||
return null;
|
||||
}
|
||||
|
||||
return itemCollector.update(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return UNIVERSAL_ITEM_STORAGE;
|
||||
}
|
||||
|
||||
public static ItemCollector<?> createItemCollector(Accessor<?> request) {
|
||||
if (request.getTarget() instanceof AbstractHorse) {
|
||||
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
|
||||
if (o instanceof AbstractHorse horse) {
|
||||
return horse.inventory;
|
||||
}
|
||||
return null;
|
||||
}, 2));
|
||||
}
|
||||
|
||||
// TODO BlockEntity like fabric's ItemStorage
|
||||
|
||||
final Container container = findContainer(request);
|
||||
if (container != null) {
|
||||
if (container instanceof ChestBlockEntity) {
|
||||
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
|
||||
if (o instanceof ChestBlockEntity blockEntity) {
|
||||
if (blockEntity.getBlockState().getBlock() instanceof ChestBlock chestBlock) {
|
||||
Container compound = null;
|
||||
if (blockEntity.getLevel() != null) {
|
||||
compound = ChestBlock.getContainer(chestBlock, blockEntity.getBlockState(), blockEntity.getLevel(), blockEntity.getBlockPos(), false);
|
||||
}
|
||||
if (compound != null) {
|
||||
return compound;
|
||||
}
|
||||
}
|
||||
return blockEntity;
|
||||
}
|
||||
return null;
|
||||
}, 0));
|
||||
}
|
||||
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(0));
|
||||
}
|
||||
|
||||
return ItemCollector.EMPTY;
|
||||
}
|
||||
|
||||
public static @Nullable Container findContainer(@NotNull Accessor<?> accessor) {
|
||||
Object target = accessor.getTarget();
|
||||
if (target == null && accessor instanceof BlockAccessor blockAccessor &&
|
||||
blockAccessor.getBlock() instanceof WorldlyContainerHolder holder) {
|
||||
return holder.getContainer(blockAccessor.getBlockState(), accessor.getLevel(), blockAccessor.getPosition());
|
||||
} else if (target instanceof Container container) {
|
||||
return container;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPriority() {
|
||||
return IServerExtensionProvider.super.getDefaultPriority() + 1000;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.LockCode;
|
||||
import net.minecraft.world.RandomizableContainer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.util.CommonUtil;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ItemCollector;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ItemStorageProvider<T extends Accessor<?>> implements IServerDataProvider<T> {
|
||||
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, Map.Entry<ResourceLocation, List<ViewGroup<ItemStack>>>> STREAM_CODEC = ViewGroup.listCodec(
|
||||
ItemStack.OPTIONAL_STREAM_CODEC);
|
||||
|
||||
private static final ResourceLocation UNIVERSAL_ITEM_STORAGE = JadeProtocol.mc_id("item_storage");
|
||||
|
||||
public static ForBlock getBlock() {
|
||||
return ForBlock.INSTANCE;
|
||||
}
|
||||
|
||||
public static ForEntity getEntity() {
|
||||
return ForEntity.INSTANCE;
|
||||
}
|
||||
|
||||
public static class ForBlock extends ItemStorageProvider<BlockAccessor> {
|
||||
private static final ForBlock INSTANCE = new ForBlock();
|
||||
}
|
||||
|
||||
public static class ForEntity extends ItemStorageProvider<EntityAccessor> {
|
||||
private static final ForEntity INSTANCE = new ForEntity();
|
||||
}
|
||||
|
||||
public static void putData(CompoundTag tag, @NotNull Accessor<?> accessor) {
|
||||
Object target = accessor.getTarget();
|
||||
Player player = accessor.getPlayer();
|
||||
Map.Entry<ResourceLocation, List<ViewGroup<ItemStack>>> entry = CommonUtil.getServerExtensionData(accessor, JadeProtocol.itemStorageProviders);
|
||||
if (entry != null) {
|
||||
List<ViewGroup<ItemStack>> groups = entry.getValue();
|
||||
for (ViewGroup<ItemStack> group : groups) {
|
||||
if (group.views.size() > ItemCollector.MAX_SIZE) {
|
||||
group.views = group.views.subList(0, ItemCollector.MAX_SIZE);
|
||||
}
|
||||
}
|
||||
tag.put(UNIVERSAL_ITEM_STORAGE.toString(), accessor.encodeAsNbt(STREAM_CODEC, entry));
|
||||
return;
|
||||
}
|
||||
if (target instanceof RandomizableContainer containerEntity && containerEntity.getLootTable() != null) {
|
||||
tag.putBoolean("Loot", true);
|
||||
} else if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
|
||||
if (te.lockKey != LockCode.NO_LOCK) {
|
||||
tag.putBoolean("Locked", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return UNIVERSAL_ITEM_STORAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendServerData(CompoundTag tag, @NotNull T accessor) {
|
||||
if (accessor.getTarget() instanceof AbstractFurnaceBlockEntity) {
|
||||
return;
|
||||
}
|
||||
putData(tag, accessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPriority() {
|
||||
return 9999;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface StreamServerDataProvider<T extends Accessor<?>, D> extends IServerDataProvider<T> {
|
||||
|
||||
@Override
|
||||
default void appendServerData(CompoundTag data, T accessor) {
|
||||
D value = streamData(accessor);
|
||||
if (value != null) {
|
||||
data.put(getUid().toString(), accessor.encodeAsNbt(streamCodec(), value));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
D streamData(T accessor);
|
||||
|
||||
StreamCodec<RegistryFriendlyByteBuf, D> streamCodec();
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum BeehiveProvider implements StreamServerDataProvider<BlockAccessor, Byte> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_BEEHIVE = JadeProtocol.mc_id("beehive");
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Byte> streamCodec() {
|
||||
return ByteBufCodecs.BYTE.cast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte streamData(@NotNull BlockAccessor accessor) {
|
||||
BeehiveBlockEntity beehive = (BeehiveBlockEntity) accessor.getBlockEntity();
|
||||
int bees = beehive.getOccupantCount();
|
||||
return (byte) (beehive.isFull() ? bees : -bees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_BEEHIVE;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum BrewingStandProvider implements StreamServerDataProvider<BlockAccessor, BrewingStandProvider.Data> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_BREWING_STAND = JadeProtocol.mc_id("brewing_stand");
|
||||
|
||||
@Override
|
||||
public @NotNull Data streamData(@NotNull BlockAccessor accessor) {
|
||||
BrewingStandBlockEntity brewingStand = (BrewingStandBlockEntity) accessor.getBlockEntity();
|
||||
return new Data(brewingStand.fuel, brewingStand.brewTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Data> streamCodec() {
|
||||
return Data.STREAM_CODEC.cast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_BREWING_STAND;
|
||||
}
|
||||
|
||||
public record Data(int fuel, int time) {
|
||||
public static final StreamCodec<ByteBuf, Data> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT,
|
||||
Data::fuel,
|
||||
ByteBufCodecs.VAR_INT,
|
||||
Data::time,
|
||||
Data::new);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import net.minecraft.world.level.block.entity.CampfireBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerExtensionProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public enum CampfireProvider implements IServerExtensionProvider<ItemStack> {
|
||||
INSTANCE;
|
||||
|
||||
private static final MapCodec<Integer> COOKING_TIME_CODEC = Codec.INT.fieldOf("jade:cooking");
|
||||
private static final ResourceLocation MC_CAMPFIRE = JadeProtocol.mc_id("campfire");
|
||||
|
||||
@Override
|
||||
public @Nullable @Unmodifiable List<ViewGroup<ItemStack>> getGroups(@NotNull Accessor<?> request) {
|
||||
if (request.getTarget() instanceof CampfireBlockEntity campfire) {
|
||||
List<ItemStack> list = Lists.newArrayList();
|
||||
for (int i = 0; i < campfire.cookingTime.length; i++) {
|
||||
ItemStack stack = campfire.getItems().get(i);
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
stack = stack.copy();
|
||||
|
||||
CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY)
|
||||
.update(NbtOps.INSTANCE, COOKING_TIME_CODEC, campfire.cookingTime[i] - campfire.cookingProgress[i])
|
||||
.getOrThrow();
|
||||
stack.set(DataComponents.CUSTOM_DATA, customData);
|
||||
|
||||
list.add(stack);
|
||||
}
|
||||
return List.of(new ViewGroup<>(list));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_CAMPFIRE;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
|
||||
import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum ChiseledBookshelfProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_CHISELED_BOOKSHELF = JadeProtocol.mc_id("chiseled_bookshelf");
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack streamData(@NotNull BlockAccessor accessor) {
|
||||
int slot = ((ChiseledBookShelfBlock) accessor.getBlock()).getHitSlot(accessor.getHitResult(), accessor.getBlockState()).orElse(-1);
|
||||
if (slot == -1) {
|
||||
return null;
|
||||
}
|
||||
return ((ChiseledBookShelfBlockEntity) accessor.getBlockEntity()).getItem(slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
|
||||
return ItemStack.OPTIONAL_STREAM_CODEC;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_CHISELED_BOOKSHELF;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.CommandBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum CommandBlockProvider implements StreamServerDataProvider<BlockAccessor, String> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_COMMAND_BLOCK = JadeProtocol.mc_id("command_block");
|
||||
|
||||
@Nullable
|
||||
public String streamData(@NotNull BlockAccessor accessor) {
|
||||
if (!accessor.getPlayer().canUseGameMasterBlocks()) {
|
||||
return null;
|
||||
}
|
||||
String command = ((CommandBlockEntity) accessor.getBlockEntity()).getCommandBlock().getCommand();
|
||||
if (command.length() > 40) {
|
||||
command = command.substring(0, 37) + "...";
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, String> streamCodec() {
|
||||
return ByteBufCodecs.STRING_UTF8.cast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_COMMAND_BLOCK;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public enum FurnaceProvider implements StreamServerDataProvider<BlockAccessor, FurnaceProvider.Data> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_FURNACE = JadeProtocol.mc_id("furnace");
|
||||
|
||||
@Override
|
||||
public @Nullable Data streamData(@NotNull BlockAccessor accessor) {
|
||||
if (!(accessor.getTarget() instanceof AbstractFurnaceBlockEntity furnace)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (furnace.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompoundTag furnaceTag = furnace.saveWithoutMetadata(accessor.getLevel().registryAccess());
|
||||
return new Data(
|
||||
furnaceTag.getInt("CookTime"),
|
||||
furnaceTag.getInt("CookTimeTotal"),
|
||||
List.of(furnace.getItem(0), furnace.getItem(1), furnace.getItem(2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, Data> streamCodec() {
|
||||
return Data.STREAM_CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_FURNACE;
|
||||
}
|
||||
|
||||
public record Data(int progress, int total, List<ItemStack> inventory) {
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, Data> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.VAR_INT,
|
||||
Data::progress,
|
||||
ByteBufCodecs.VAR_INT,
|
||||
Data::total,
|
||||
ItemStack.OPTIONAL_LIST_STREAM_CODEC,
|
||||
Data::inventory,
|
||||
Data::new);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum HopperLockProvider implements StreamServerDataProvider<BlockAccessor, Boolean> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_HOPPER_LOCK = JadeProtocol.mc_id("hopper_lock");
|
||||
|
||||
@Override
|
||||
public Boolean streamData(@NotNull BlockAccessor accessor) {
|
||||
return !accessor.getBlockState().getValue(BlockStateProperties.ENABLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Boolean> streamCodec() {
|
||||
return ByteBufCodecs.BOOL.cast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_HOPPER_LOCK;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.JukeboxBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum JukeboxProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_JUKEBOX = JadeProtocol.mc_id("jukebox");
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack streamData(BlockAccessor accessor) {
|
||||
return ((JukeboxBlockEntity) accessor.getBlockEntity()).getTheItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
|
||||
return ItemStack.OPTIONAL_STREAM_CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_JUKEBOX;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum LecternProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_LECTERN = JadeProtocol.mc_id("lectern");
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack streamData(@NotNull BlockAccessor accessor) {
|
||||
return ((LecternBlockEntity) accessor.getBlockEntity()).getBook();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
|
||||
return ItemStack.OPTIONAL_STREAM_CODEC;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_LECTERN;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum MobSpawnerCooldownProvider implements StreamServerDataProvider<BlockAccessor, Integer> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_MOB_SPAWNER_COOLDOWN = JadeProtocol.mc_id("mob_spawner.cooldown");
|
||||
|
||||
@Override
|
||||
public @Nullable Integer streamData(@NotNull BlockAccessor accessor) {
|
||||
TrialSpawnerBlockEntity spawner = (TrialSpawnerBlockEntity) accessor.getBlockEntity();
|
||||
TrialSpawnerData spawnerData = spawner.getTrialSpawner().getData();
|
||||
ServerLevel level = ((ServerLevel) accessor.getLevel());
|
||||
if (spawner.getTrialSpawner().canSpawnInLevel(level) && level.getGameTime() < spawnerData.cooldownEndsAt) {
|
||||
return (int) (spawnerData.cooldownEndsAt - level.getGameTime());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
|
||||
return ByteBufCodecs.VAR_INT.cast();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_MOB_SPAWNER_COOLDOWN;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.ComponentSerialization;
|
||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.Nameable;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.world.level.block.state.properties.ChestType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public abstract class ObjectNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
|
||||
|
||||
private static final ResourceLocation CORE_OBJECT_NAME = JadeProtocol.id("object_name");
|
||||
|
||||
public static class ForBlock extends ObjectNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
|
||||
public static final ForBlock INSTANCE = new ForBlock();
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Component streamData(@NotNull BlockAccessor accessor) {
|
||||
if (!(accessor.getBlockEntity() instanceof Nameable nameable)) {
|
||||
return null;
|
||||
}
|
||||
if (nameable instanceof ChestBlockEntity && accessor.getBlock() instanceof ChestBlock && accessor.getBlockState().getValue(ChestBlock.TYPE) != ChestType.SINGLE) {
|
||||
MenuProvider menuProvider = accessor.getBlockState().getMenuProvider(accessor.getLevel(), accessor.getPosition());
|
||||
if (menuProvider != null) {
|
||||
Component name = menuProvider.getDisplayName();
|
||||
if (!(name.getContents() instanceof TranslatableContents contents) || !"container.chestDouble".equals(contents.getKey())) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
} else if (nameable.hasCustomName()) {
|
||||
return nameable.getDisplayName();
|
||||
}
|
||||
return accessor.getBlockEntity().components().get(DataComponents.ITEM_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, Component> streamCodec() {
|
||||
return ComponentSerialization.STREAM_CODEC;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return CORE_OBJECT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPriority() {
|
||||
return -10100;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.block;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.CalibratedSculkSensorBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.CalibratedSculkSensorBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ComparatorBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
|
||||
|
||||
public enum RedstoneProvider implements IServerDataProvider<BlockAccessor> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_REDSTONE = JadeProtocol.mc_id("redstone");
|
||||
|
||||
@Override
|
||||
public void appendServerData(CompoundTag data, @NotNull BlockAccessor accessor) {
|
||||
BlockEntity blockEntity = accessor.getBlockEntity();
|
||||
if (blockEntity instanceof ComparatorBlockEntity comparator) {
|
||||
data.putInt("Signal", comparator.getOutputSignal());
|
||||
} else if (blockEntity instanceof CalibratedSculkSensorBlockEntity) {
|
||||
Direction direction = accessor.getBlockState().getValue(CalibratedSculkSensorBlock.FACING).getOpposite();
|
||||
int signal = accessor.getLevel().getSignal(accessor.getPosition().relative(direction), direction);
|
||||
data.putInt("Signal", signal);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_REDSTONE;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.OwnableEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
import org.leavesmc.leaves.protocol.jade.util.CommonUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public enum AnimalOwnerProvider implements StreamServerDataProvider<EntityAccessor, String> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_ANIMAL_OWNER = JadeProtocol.mc_id("animal_owner");
|
||||
|
||||
@Override
|
||||
public String streamData(@NotNull EntityAccessor accessor) {
|
||||
return CommonUtil.getLastKnownUsername(getOwnerUUID(accessor.getEntity()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, String> streamCodec() {
|
||||
return ByteBufCodecs.STRING_UTF8.cast();
|
||||
}
|
||||
|
||||
public static UUID getOwnerUUID(Entity entity) {
|
||||
if (entity instanceof OwnableEntity ownableEntity) {
|
||||
return ownableEntity.getOwnerUUID();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_ANIMAL_OWNER;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
import net.minecraft.world.entity.animal.allay.Allay;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum MobBreedingProvider implements StreamServerDataProvider<EntityAccessor, Integer> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_MOB_BREEDING = JadeProtocol.mc_id("mob_breeding");
|
||||
|
||||
@Override
|
||||
public @Nullable Integer streamData(@NotNull EntityAccessor accessor) {
|
||||
int time = 0;
|
||||
Entity entity = accessor.getEntity();
|
||||
if (entity instanceof Allay allay) {
|
||||
if (allay.duplicationCooldown > 0 && allay.duplicationCooldown < Integer.MAX_VALUE) {
|
||||
time = (int) allay.duplicationCooldown;
|
||||
}
|
||||
} else {
|
||||
time = ((Animal) entity).getAge();
|
||||
}
|
||||
return time > 0 ? time : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
|
||||
return ByteBufCodecs.VAR_INT.cast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_MOB_BREEDING;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.AgeableMob;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.animal.frog.Tadpole;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum MobGrowthProvider implements StreamServerDataProvider<EntityAccessor, Integer> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_MOB_GROWTH = JadeProtocol.mc_id("mob_growth");
|
||||
|
||||
@Override
|
||||
public @Nullable Integer streamData(@NotNull EntityAccessor accessor) {
|
||||
int time = -1;
|
||||
Entity entity = accessor.getEntity();
|
||||
if (entity instanceof AgeableMob ageable) {
|
||||
time = -ageable.getAge();
|
||||
} else if (entity instanceof Tadpole tadpole) {
|
||||
time = tadpole.getTicksLeftUntilAdult();
|
||||
}
|
||||
return time > 0 ? time : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
|
||||
return ByteBufCodecs.VAR_INT.cast();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_MOB_GROWTH;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.animal.Chicken;
|
||||
import net.minecraft.world.entity.animal.armadillo.Armadillo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
|
||||
|
||||
public enum NextEntityDropProvider implements IServerDataProvider<EntityAccessor> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_NEXT_ENTITY_DROP = JadeProtocol.mc_id("next_entity_drop");
|
||||
|
||||
@Override
|
||||
public void appendServerData(CompoundTag tag, @NotNull EntityAccessor accessor) {
|
||||
int max = 24000 * 2;
|
||||
if (accessor.getEntity() instanceof Chicken chicken) {
|
||||
if (!chicken.isBaby() && chicken.eggTime < max) {
|
||||
tag.putInt("NextEggIn", chicken.eggTime);
|
||||
}
|
||||
} else if (accessor.getEntity() instanceof Armadillo armadillo) {
|
||||
if (!armadillo.isBaby() && armadillo.scuteTime < max) {
|
||||
tag.putInt("NextScuteIn", armadillo.scuteTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_NEXT_ENTITY_DROP;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum PetArmorProvider implements StreamServerDataProvider<EntityAccessor, ItemStack> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_PET_ARMOR = JadeProtocol.mc_id("pet_armor");
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ItemStack streamData(@NotNull EntityAccessor accessor) {
|
||||
ItemStack armor = ((Mob) accessor.getEntity()).getBodyArmorItem();
|
||||
return armor.isEmpty() ? null : armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
|
||||
return ItemStack.OPTIONAL_STREAM_CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_PET_ARMOR;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public enum StatusEffectsProvider implements StreamServerDataProvider<EntityAccessor, List<MobEffectInstance>> {
|
||||
INSTANCE;
|
||||
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, List<MobEffectInstance>> STREAM_CODEC = ByteBufCodecs.<RegistryFriendlyByteBuf, MobEffectInstance>list()
|
||||
.apply(MobEffectInstance.STREAM_CODEC);
|
||||
private static final ResourceLocation MC_POTION_EFFECTS = JadeProtocol.mc_id("potion_effects");
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<MobEffectInstance> streamData(@NotNull EntityAccessor accessor) {
|
||||
List<MobEffectInstance> effects = ((LivingEntity) accessor.getEntity()).getActiveEffects()
|
||||
.stream()
|
||||
.filter(MobEffectInstance::isVisible)
|
||||
.toList();
|
||||
return effects.isEmpty() ? null : effects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, List<MobEffectInstance>> streamCodec() {
|
||||
return STREAM_CODEC;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_POTION_EFFECTS;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.monster.ZombieVillager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
|
||||
|
||||
public enum ZombieVillagerProvider implements StreamServerDataProvider<EntityAccessor, Integer> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation MC_ZOMBIE_VILLAGER = JadeProtocol.mc_id("zombie_villager");
|
||||
|
||||
@Override
|
||||
public @Nullable Integer streamData(@NotNull EntityAccessor accessor) {
|
||||
int time = ((ZombieVillager) accessor.getEntity()).villagerConversionTime;
|
||||
return time > 0 ? time : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
|
||||
return ByteBufCodecs.VAR_INT.cast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_ZOMBIE_VILLAGER;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.tool;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ShearsToolHandler extends SimpleToolHandler {
|
||||
|
||||
private static final ShearsToolHandler INSTANCE = new ShearsToolHandler();
|
||||
|
||||
public static ShearsToolHandler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public ShearsToolHandler() {
|
||||
super(JadeProtocol.id("shears"), List.of(Items.SHEARS.getDefaultInstance()), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack test(BlockState state, Level world, BlockPos pos) {
|
||||
if (state.is(Blocks.TRIPWIRE)) {
|
||||
return tools.getFirst();
|
||||
}
|
||||
return super.test(state, world, pos);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.tool;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.Tool;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleToolHandler implements ToolHandler {
|
||||
|
||||
protected final List<ItemStack> tools = Lists.newArrayList();
|
||||
private final ResourceLocation uid;
|
||||
private final boolean skipInstaBreakingBlock;
|
||||
|
||||
protected SimpleToolHandler(ResourceLocation uid, @NotNull List<ItemStack> tools, boolean skipInstaBreakingBlock) {
|
||||
this.uid = uid;
|
||||
Preconditions.checkArgument(!tools.isEmpty(), "tools cannot be empty");
|
||||
this.tools.addAll(tools);
|
||||
this.skipInstaBreakingBlock = skipInstaBreakingBlock;
|
||||
}
|
||||
|
||||
@Contract("_, _ -> new")
|
||||
public static @NotNull SimpleToolHandler create(ResourceLocation uid, List<Item> tools) {
|
||||
return create(uid, tools, true);
|
||||
}
|
||||
|
||||
@Contract("_, _, _ -> new")
|
||||
public static @NotNull SimpleToolHandler create(ResourceLocation uid, List<Item> tools, boolean skipInstaBreakingBlock) {
|
||||
return new SimpleToolHandler(uid, Lists.transform(tools, Item::getDefaultInstance), skipInstaBreakingBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack test(BlockState state, Level world, BlockPos pos) {
|
||||
if (skipInstaBreakingBlock && !state.requiresCorrectToolForDrops() && state.getDestroySpeed(world, pos) == 0) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
return test(state);
|
||||
}
|
||||
|
||||
public ItemStack test(BlockState state) {
|
||||
for (ItemStack toolItem : tools) {
|
||||
if (toolItem.isCorrectToolForDrops(state)) {
|
||||
return toolItem;
|
||||
}
|
||||
Tool tool = toolItem.get(DataComponents.TOOL);
|
||||
if (tool != null && tool.getMiningSpeed(state) > tool.defaultMiningSpeed()) {
|
||||
return toolItem;
|
||||
}
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getTools() {
|
||||
return tools;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.tool;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ToolHandler extends IJadeProvider {
|
||||
|
||||
ItemStack test(BlockState state, Level world, BlockPos pos);
|
||||
|
||||
List<ItemStack> getTools();
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.boss.EnderDragonPart;
|
||||
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.SkullBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerExtensionProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CommonUtil {
|
||||
|
||||
public static @NotNull ResourceLocation getId(Block block) {
|
||||
return BuiltInRegistries.BLOCK.getKey(block);
|
||||
}
|
||||
|
||||
public static Entity wrapPartEntityParent(Entity target) {
|
||||
if (target instanceof EnderDragonPart part) {
|
||||
return part.parentMob;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public static Entity getPartEntity(Entity parent, int index) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
if (index < 0) {
|
||||
return parent;
|
||||
}
|
||||
if (parent instanceof EnderDragon dragon) {
|
||||
EnderDragonPart[] parts = dragon.getSubEntities();
|
||||
if (index < parts.length) {
|
||||
return parts[index];
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static String getLastKnownUsername(@Nullable UUID uuid) {
|
||||
if (uuid == null) {
|
||||
return null;
|
||||
}
|
||||
Optional<GameProfile> optional = SkullBlockEntity.fetchGameProfile(String.valueOf(uuid)).getNow(Optional.empty());
|
||||
return optional.map(GameProfile::getName).orElse(null);
|
||||
}
|
||||
|
||||
|
||||
public static <T> Map.Entry<ResourceLocation, List<ViewGroup<T>>> getServerExtensionData(
|
||||
Accessor<?> accessor,
|
||||
WrappedHierarchyLookup<IServerExtensionProvider<T>> lookup) {
|
||||
for (var provider : lookup.wrappedGet(accessor)) {
|
||||
List<ViewGroup<T>> groups;
|
||||
try {
|
||||
groups = provider.getGroups(accessor);
|
||||
} catch (Exception e) {
|
||||
LeavesLogger.LOGGER.severe(e.toString());
|
||||
continue;
|
||||
}
|
||||
if (groups != null) {
|
||||
return Map.entry(provider.getUid(), groups);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class HierarchyLookup<T extends IJadeProvider> implements IHierarchyLookup<T> {
|
||||
|
||||
private final Class<?> baseClass;
|
||||
private final Cache<Class<?>, List<T>> resultCache = CacheBuilder.newBuilder().build();
|
||||
private final boolean singleton;
|
||||
protected boolean idMapped;
|
||||
@Nullable
|
||||
protected IdMapper<T> idMapper;
|
||||
private ListMultimap<Class<?>, T> objects = ArrayListMultimap.create();
|
||||
|
||||
public HierarchyLookup(Class<?> baseClass) {
|
||||
this(baseClass, false);
|
||||
}
|
||||
|
||||
public HierarchyLookup(Class<?> baseClass, boolean singleton) {
|
||||
this.baseClass = baseClass;
|
||||
this.singleton = singleton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void idMapped() {
|
||||
this.idMapped = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public IdMapper<T> idMapper() {
|
||||
return idMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Class<?> clazz, T provider) {
|
||||
Preconditions.checkArgument(isClassAcceptable(clazz), "Class %s is not acceptable", clazz);
|
||||
Objects.requireNonNull(provider.getUid());
|
||||
JadeProtocol.priorities.put(provider);
|
||||
objects.put(clazz, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClassAcceptable(Class<?> clazz) {
|
||||
return baseClass.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> get(Class<?> clazz) {
|
||||
try {
|
||||
return resultCache.get(clazz, () -> {
|
||||
List<T> list = Lists.newArrayList();
|
||||
getInternal(clazz, list);
|
||||
list = ImmutableList.sortedCopyOf(Comparator.comparingInt(JadeProtocol.priorities::byValue), list);
|
||||
if (singleton && !list.isEmpty()) {
|
||||
return ImmutableList.of(list.getFirst());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
LeavesLogger.LOGGER.warning("HierarchyLookup error", e);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private void getInternal(Class<?> clazz, List<T> list) {
|
||||
if (clazz != baseClass && clazz != Object.class) {
|
||||
getInternal(clazz.getSuperclass(), list);
|
||||
}
|
||||
list.addAll(objects.get(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return objects.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Map.Entry<Class<?>, Collection<T>>> entries() {
|
||||
return objects.asMap().entrySet().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
resultCache.invalidateAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadComplete(PriorityStore<ResourceLocation, IJadeProvider> priorityStore) {
|
||||
objects.asMap().forEach((clazz, list) -> {
|
||||
if (list.size() < 2) {
|
||||
return;
|
||||
}
|
||||
Set<ResourceLocation> set = Sets.newHashSetWithExpectedSize(list.size());
|
||||
for (T provider : list) {
|
||||
if (set.contains(provider.getUid())) {
|
||||
throw new IllegalStateException("Duplicate UID: %s for %s".formatted(provider.getUid(), list.stream()
|
||||
.filter(p -> p.getUid().equals(provider.getUid()))
|
||||
.map(p -> p.getClass().getName())
|
||||
.toList()
|
||||
));
|
||||
}
|
||||
set.add(provider.getUid());
|
||||
}
|
||||
});
|
||||
|
||||
objects = ImmutableListMultimap.<Class<?>, T>builder()
|
||||
.orderValuesBy(Comparator.comparingInt(priorityStore::byValue))
|
||||
.putAll(objects)
|
||||
.build();
|
||||
|
||||
if (idMapped) {
|
||||
idMapper = createIdMapper();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface IHierarchyLookup<T extends IJadeProvider> {
|
||||
|
||||
default IHierarchyLookup<? extends T> cast() {
|
||||
return this;
|
||||
}
|
||||
|
||||
void idMapped();
|
||||
|
||||
@Nullable
|
||||
IdMapper<T> idMapper();
|
||||
|
||||
default List<ResourceLocation> mappedIds() {
|
||||
return Streams.stream(Objects.requireNonNull(idMapper()))
|
||||
.map(IJadeProvider::getUid)
|
||||
.toList();
|
||||
}
|
||||
|
||||
void register(Class<?> clazz, T provider);
|
||||
|
||||
boolean isClassAcceptable(Class<?> clazz);
|
||||
|
||||
default List<T> get(Object obj) {
|
||||
if (obj == null) {
|
||||
return List.of();
|
||||
}
|
||||
return get(obj.getClass());
|
||||
}
|
||||
|
||||
List<T> get(Class<?> clazz);
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
Stream<Map.Entry<Class<?>, Collection<T>>> entries();
|
||||
|
||||
void invalidate();
|
||||
|
||||
void loadComplete(PriorityStore<ResourceLocation, IJadeProvider> priorityStore);
|
||||
|
||||
default IdMapper<T> createIdMapper() {
|
||||
List<T> list = entries().flatMap(entry -> entry.getValue().stream()).toList();
|
||||
IdMapper<T> idMapper = idMapper();
|
||||
if (idMapper == null) {
|
||||
idMapper = new IdMapper<>(list.size());
|
||||
}
|
||||
for (T provider : list) {
|
||||
if (idMapper.getId(provider) == IdMapper.DEFAULT) {
|
||||
idMapper.add(provider);
|
||||
}
|
||||
}
|
||||
return idMapper;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.CustomData;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ItemCollector<T> {
|
||||
|
||||
public static final int MAX_SIZE = 54;
|
||||
public static final ItemCollector<?> EMPTY = new ItemCollector<>(null);
|
||||
private static final Predicate<ItemStack> NON_EMPTY = stack -> {
|
||||
if (stack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
||||
if (customData.contains("CustomModelData")) {
|
||||
CompoundTag tag = customData.copyTag();
|
||||
for (String key : tag.getAllKeys()) {
|
||||
if (key.toLowerCase(Locale.ENGLISH).endsWith("clear") && tag.getBoolean(key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
private final Object2IntLinkedOpenHashMap<ItemDefinition> items = new Object2IntLinkedOpenHashMap<>();
|
||||
private final ItemIterator<T> iterator;
|
||||
public long version;
|
||||
public long lastTimeFinished;
|
||||
public boolean lastTimeIsEmpty;
|
||||
public List<ViewGroup<ItemStack>> mergedResult;
|
||||
|
||||
public ItemCollector(ItemIterator<T> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
public List<ViewGroup<ItemStack>> update(Accessor<?> request) {
|
||||
if (iterator == null) {
|
||||
return null;
|
||||
}
|
||||
T container = iterator.find(request.getTarget());
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
long currentVersion = iterator.getVersion(container);
|
||||
long gameTime = request.getLevel().getGameTime();
|
||||
if (mergedResult != null && iterator.isFinished()) {
|
||||
if (version == currentVersion) {
|
||||
return mergedResult; // content not changed
|
||||
}
|
||||
if (lastTimeFinished + 5 > gameTime) {
|
||||
return mergedResult; // avoid update too frequently
|
||||
}
|
||||
iterator.reset();
|
||||
}
|
||||
AtomicInteger count = new AtomicInteger();
|
||||
iterator.populate(container).forEach(stack -> {
|
||||
count.incrementAndGet();
|
||||
if (NON_EMPTY.test(stack)) {
|
||||
ItemDefinition def = new ItemDefinition(stack);
|
||||
items.addTo(def, stack.getCount());
|
||||
}
|
||||
});
|
||||
iterator.afterPopulate(count.get());
|
||||
if (mergedResult != null && !iterator.isFinished()) {
|
||||
updateCollectingProgress(mergedResult.getFirst());
|
||||
return mergedResult;
|
||||
}
|
||||
List<ItemStack> partialResult = items.object2IntEntrySet().stream().limit(MAX_SIZE).map(entry -> {
|
||||
ItemDefinition def = entry.getKey();
|
||||
return def.toStack(entry.getIntValue());
|
||||
}).toList();
|
||||
List<ViewGroup<ItemStack>> groups = List.of(updateCollectingProgress(new ViewGroup<>(partialResult)));
|
||||
if (iterator.isFinished()) {
|
||||
mergedResult = groups;
|
||||
lastTimeIsEmpty = mergedResult.getFirst().views.isEmpty();
|
||||
version = currentVersion;
|
||||
lastTimeFinished = gameTime;
|
||||
items.clear();
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
protected ViewGroup<ItemStack> updateCollectingProgress(ViewGroup<ItemStack> group) {
|
||||
if (lastTimeIsEmpty && group.views.isEmpty()) {
|
||||
return group;
|
||||
}
|
||||
float progress = iterator.getCollectingProgress();
|
||||
CompoundTag data = group.getExtraData();
|
||||
if (Float.isNaN(progress) || progress >= 1) {
|
||||
data.remove("Collecting");
|
||||
} else {
|
||||
data.putFloat("Collecting", progress);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
public record ItemDefinition(Item item, DataComponentPatch components) {
|
||||
ItemDefinition(ItemStack stack) {
|
||||
this(stack.getItem(), stack.getComponentsPatch());
|
||||
}
|
||||
|
||||
public ItemStack toStack(int count) {
|
||||
ItemStack itemStack = new ItemStack(item, count);
|
||||
itemStack.applyComponents(components);
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class ItemIterator<T> {
|
||||
|
||||
public static final AtomicLong version = new AtomicLong();
|
||||
protected final Function<Object, @Nullable T> containerFinder;
|
||||
protected final int fromIndex;
|
||||
protected boolean finished;
|
||||
protected int currentIndex;
|
||||
|
||||
protected ItemIterator(Function<Object, @Nullable T> containerFinder, int fromIndex) {
|
||||
this.containerFinder = containerFinder;
|
||||
this.currentIndex = this.fromIndex = fromIndex;
|
||||
}
|
||||
|
||||
public @Nullable T find(Object target) {
|
||||
return containerFinder.apply(target);
|
||||
}
|
||||
|
||||
public final boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public long getVersion(T container) {
|
||||
return version.getAndIncrement();
|
||||
}
|
||||
|
||||
public abstract Stream<ItemStack> populate(T container);
|
||||
|
||||
public void reset() {
|
||||
currentIndex = fromIndex;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
public void afterPopulate(int count) {
|
||||
currentIndex += count;
|
||||
if (count == 0 || currentIndex >= 10000) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
public float getCollectingProgress() {
|
||||
return Float.NaN;
|
||||
}
|
||||
|
||||
public static abstract class SlottedItemIterator<T> extends ItemIterator<T> {
|
||||
protected float progress;
|
||||
|
||||
public SlottedItemIterator(Function<Object, @Nullable T> containerFinder, int fromIndex) {
|
||||
super(containerFinder, fromIndex);
|
||||
}
|
||||
|
||||
protected abstract int getSlotCount(T container);
|
||||
|
||||
protected abstract ItemStack getItemInSlot(T container, int slot);
|
||||
|
||||
@Override
|
||||
public Stream<ItemStack> populate(T container) {
|
||||
int slotCount = getSlotCount(container);
|
||||
int toIndex = currentIndex + ItemCollector.MAX_SIZE * 2;
|
||||
if (toIndex >= slotCount) {
|
||||
toIndex = slotCount;
|
||||
finished = true;
|
||||
}
|
||||
progress = (float) (currentIndex - fromIndex) / (slotCount - fromIndex);
|
||||
return IntStream.range(currentIndex, toIndex).mapToObj(slot -> getItemInSlot(container, slot));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCollectingProgress() {
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ContainerItemIterator extends SlottedItemIterator<Container> {
|
||||
public ContainerItemIterator(int fromIndex) {
|
||||
this(Container.class::cast, fromIndex);
|
||||
}
|
||||
|
||||
public ContainerItemIterator(Function<Object, @Nullable Container> containerFinder, int fromIndex) {
|
||||
super(containerFinder, fromIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSlotCount(Container container) {
|
||||
return container.getContainerSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemStack getItemInSlot(Container container, int slot) {
|
||||
return container.getItem(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class JadeCodec {
|
||||
|
||||
public static final StreamCodec<ByteBuf, Object> PRIMITIVE_STREAM_CODEC = new StreamCodec<>() {
|
||||
@Override
|
||||
public @NotNull Object decode(@NotNull ByteBuf buf) {
|
||||
byte b = buf.readByte();
|
||||
if (b == 0) {
|
||||
return false;
|
||||
} else if (b == 1) {
|
||||
return true;
|
||||
} else if (b == 2) {
|
||||
return ByteBufCodecs.VAR_INT.decode(buf);
|
||||
} else if (b == 3) {
|
||||
return ByteBufCodecs.FLOAT.decode(buf);
|
||||
} else if (b == 4) {
|
||||
return ByteBufCodecs.STRING_UTF8.decode(buf);
|
||||
} else if (b > 20) {
|
||||
return b - 20;
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown primitive type: " + b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(@NotNull ByteBuf buf, @NotNull Object o) {
|
||||
switch (o) {
|
||||
case Boolean b -> buf.writeByte(b ? 1 : 0);
|
||||
case Number n -> {
|
||||
float f = n.floatValue();
|
||||
if (f != (int) f) {
|
||||
buf.writeByte(3);
|
||||
ByteBufCodecs.FLOAT.encode(buf, f);
|
||||
}
|
||||
int i = n.intValue();
|
||||
if (i <= Byte.MAX_VALUE - 20 && i >= 0) {
|
||||
buf.writeByte(i + 20);
|
||||
} else {
|
||||
ByteBufCodecs.VAR_INT.encode(buf, i);
|
||||
}
|
||||
}
|
||||
case String s -> {
|
||||
buf.writeByte(4);
|
||||
ByteBufCodecs.STRING_UTF8.encode(buf, s);
|
||||
}
|
||||
case Enum<?> anEnum -> {
|
||||
buf.writeByte(4);
|
||||
ByteBufCodecs.STRING_UTF8.encode(buf, anEnum.name());
|
||||
}
|
||||
case null -> throw new NullPointerException();
|
||||
default ->
|
||||
throw new IllegalArgumentException("Unknown primitive type: %s (%s)".formatted(o, o.getClass()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderGetter;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.storage.loot.LootPool;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
import net.minecraft.world.level.storage.loot.entries.AlternativesEntry;
|
||||
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||
import net.minecraft.world.level.storage.loot.entries.NestedLootTable;
|
||||
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.jade.tool.ShearsToolHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LootTableMineableCollector {
|
||||
|
||||
private final HolderGetter<LootTable> lootRegistry;
|
||||
private final ItemStack toolItem;
|
||||
|
||||
public LootTableMineableCollector(HolderGetter<LootTable> lootRegistry, ItemStack toolItem) {
|
||||
this.lootRegistry = lootRegistry;
|
||||
this.toolItem = toolItem;
|
||||
}
|
||||
|
||||
public static @NotNull List<Block> execute(HolderGetter<LootTable> lootRegistry, ItemStack toolItem) {
|
||||
LootTableMineableCollector collector = new LootTableMineableCollector(lootRegistry, toolItem);
|
||||
List<Block> list = Lists.newArrayList();
|
||||
for (Block block : BuiltInRegistries.BLOCK) {
|
||||
if (!ShearsToolHandler.getInstance().test(block.defaultBlockState()).isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.getLootTable().isPresent()) {
|
||||
LootTable lootTable = lootRegistry.get(block.getLootTable().get()).map(Holder::value).orElse(null);
|
||||
if (collector.doLootTable(lootTable)) {
|
||||
list.add(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean doLootTable(LootTable lootTable) {
|
||||
if (lootTable == null || lootTable == LootTable.EMPTY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (LootPool pool : lootTable.pools) {
|
||||
if (doLootPool(pool)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean doLootPool(@NotNull LootPool lootPool) {
|
||||
for (LootPoolEntryContainer entry : lootPool.entries) {
|
||||
if (doLootPoolEntry(entry)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean doLootPoolEntry(LootPoolEntryContainer entry) {
|
||||
if (entry instanceof AlternativesEntry alternativesEntry) {
|
||||
for (LootPoolEntryContainer child : alternativesEntry.children) {
|
||||
if (doLootPoolEntry(child)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (entry instanceof NestedLootTable nestedLootTable) {
|
||||
LootTable lootTable = nestedLootTable.contents.map($ -> lootRegistry.get($).map(Holder::value).orElse(null), Function.identity());
|
||||
return doLootTable(lootTable);
|
||||
} else {
|
||||
return isCorrectConditions(entry.conditions, toolItem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isCorrectConditions(@NotNull List<LootItemCondition> conditions, ItemStack toolItem) {
|
||||
if (conditions.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LootItemCondition condition = conditions.getFirst();
|
||||
if (condition instanceof MatchTool(Optional<ItemPredicate> predicate)) {
|
||||
ItemPredicate itemPredicate = predicate.orElse(null);
|
||||
return itemPredicate != null && itemPredicate.test(toolItem);
|
||||
} else if (condition instanceof AnyOfCondition anyOfCondition) {
|
||||
for (LootItemCondition child : anyOfCondition.terms) {
|
||||
if (isCorrectConditions(List.of(child), toolItem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class PairHierarchyLookup<T extends IJadeProvider> implements IHierarchyLookup<T> {
|
||||
|
||||
public final IHierarchyLookup<T> first;
|
||||
public final IHierarchyLookup<T> second;
|
||||
private final Cache<Pair<Class<?>, Class<?>>, List<T>> mergedCache = CacheBuilder.newBuilder().build();
|
||||
protected boolean idMapped;
|
||||
@Nullable
|
||||
protected IdMapper<T> idMapper;
|
||||
|
||||
public PairHierarchyLookup(IHierarchyLookup<T> first, IHierarchyLookup<T> second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <ANY> List<ANY> getMerged(Object first, Object second) {
|
||||
Objects.requireNonNull(first);
|
||||
Objects.requireNonNull(second);
|
||||
try {
|
||||
return (List<ANY>) mergedCache.get(Pair.of(first.getClass(), second.getClass()), () -> {
|
||||
List<T> firstList = this.first.get(first);
|
||||
List<T> secondList = this.second.get(second);
|
||||
if (firstList.isEmpty()) {
|
||||
return secondList;
|
||||
} else if (secondList.isEmpty()) {
|
||||
return firstList;
|
||||
}
|
||||
return ImmutableList.sortedCopyOf(
|
||||
Comparator.comparingInt(JadeProtocol.priorities::byValue),
|
||||
Iterables.concat(firstList, secondList)
|
||||
);
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
LeavesLogger.LOGGER.severe(e.toString());
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void idMapped() {
|
||||
idMapped = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IdMapper<T> idMapper() {
|
||||
return idMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Class<?> clazz, T provider) {
|
||||
if (first.isClassAcceptable(clazz)) {
|
||||
first.register(clazz, provider);
|
||||
} else if (second.isClassAcceptable(clazz)) {
|
||||
second.register(clazz, provider);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Class " + clazz + " is not acceptable");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClassAcceptable(Class<?> clazz) {
|
||||
return first.isClassAcceptable(clazz) || second.isClassAcceptable(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> get(Class<?> clazz) {
|
||||
List<T> result = first.get(clazz);
|
||||
if (result.isEmpty()) {
|
||||
result = second.get(clazz);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return first.isEmpty() && second.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Map.Entry<Class<?>, Collection<T>>> entries() {
|
||||
return Stream.concat(first.entries(), second.entries());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
first.invalidate();
|
||||
second.invalidate();
|
||||
mergedCache.invalidateAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadComplete(PriorityStore<ResourceLocation, IJadeProvider> priorityStore) {
|
||||
first.loadComplete(priorityStore);
|
||||
second.loadComplete(priorityStore);
|
||||
if (idMapped) {
|
||||
idMapper = createIdMapper();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
public class PriorityStore<K, V> {
|
||||
|
||||
private final Object2IntMap<K> priorities = new Object2IntLinkedOpenHashMap<>();
|
||||
private final Function<V, K> keyGetter;
|
||||
private final ToIntFunction<V> defaultPriorityGetter;
|
||||
|
||||
public PriorityStore(ToIntFunction<V> defaultPriorityGetter, Function<V, K> keyGetter) {
|
||||
this.defaultPriorityGetter = defaultPriorityGetter;
|
||||
this.keyGetter = keyGetter;
|
||||
}
|
||||
|
||||
public void put(V provider) {
|
||||
Objects.requireNonNull(provider);
|
||||
put(provider, defaultPriorityGetter.applyAsInt(provider));
|
||||
}
|
||||
|
||||
public void put(V provider, int priority) {
|
||||
Objects.requireNonNull(provider);
|
||||
K uid = keyGetter.apply(provider);
|
||||
Objects.requireNonNull(uid);
|
||||
priorities.put(uid, priority);
|
||||
}
|
||||
|
||||
public int byValue(V value) {
|
||||
return byKey(keyGetter.apply(value));
|
||||
}
|
||||
|
||||
public int byKey(K id) {
|
||||
return priorities.getInt(id);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ViewGroup<T> {
|
||||
|
||||
public static <B extends ByteBuf, T> StreamCodec<B, ViewGroup<T>> codec(StreamCodec<B, T> viewCodec) {
|
||||
return StreamCodec.composite(
|
||||
ByteBufCodecs.<B, T>list().apply(viewCodec),
|
||||
$ -> $.views,
|
||||
ByteBufCodecs.optional(ByteBufCodecs.STRING_UTF8),
|
||||
$ -> Optional.ofNullable($.id),
|
||||
ByteBufCodecs.optional(ByteBufCodecs.COMPOUND_TAG),
|
||||
$ -> Optional.ofNullable($.extraData),
|
||||
ViewGroup::new);
|
||||
}
|
||||
|
||||
public static <B extends ByteBuf, T> StreamCodec<B, Map.Entry<ResourceLocation, List<ViewGroup<T>>>> listCodec(StreamCodec<B, T> viewCodec) {
|
||||
return StreamCodec.composite(
|
||||
ResourceLocation.STREAM_CODEC,
|
||||
Map.Entry::getKey,
|
||||
ByteBufCodecs.<B, ViewGroup<T>>list().apply(codec(viewCodec)),
|
||||
Map.Entry::getValue,
|
||||
Map::entry);
|
||||
}
|
||||
|
||||
public List<T> views;
|
||||
@Nullable
|
||||
public String id;
|
||||
@Nullable
|
||||
protected CompoundTag extraData;
|
||||
|
||||
public ViewGroup(List<T> views) {
|
||||
this(views, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public ViewGroup(List<T> views, Optional<String> id, Optional<CompoundTag> extraData) {
|
||||
this.views = views;
|
||||
this.id = id.orElse(null);
|
||||
this.extraData = extraData.orElse(null);
|
||||
}
|
||||
|
||||
public CompoundTag getExtraData() {
|
||||
if (extraData == null) {
|
||||
extraData = new CompoundTag();
|
||||
}
|
||||
return extraData;
|
||||
}
|
||||
|
||||
public void setProgress(float progress) {
|
||||
getExtraData().putFloat("Progress", progress);
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class WrappedHierarchyLookup<T extends IJadeProvider> extends HierarchyLookup<T> {
|
||||
|
||||
public final List<Pair<IHierarchyLookup<T>, Function<Accessor<?>, @Nullable Object>>> overrides = Lists.newArrayList();
|
||||
private boolean empty = true;
|
||||
|
||||
public WrappedHierarchyLookup() {
|
||||
super(Object.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T extends IJadeProvider> WrappedHierarchyLookup<T> forAccessor() {
|
||||
WrappedHierarchyLookup<T> lookup = new WrappedHierarchyLookup<>();
|
||||
lookup.overrides.add(Pair.of(
|
||||
new HierarchyLookup<>(Block.class), accessor -> {
|
||||
if (accessor instanceof BlockAccessor blockAccessor) {
|
||||
return blockAccessor.getBlock();
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
return lookup;
|
||||
}
|
||||
|
||||
public List<T> wrappedGet(Accessor<?> accessor) {
|
||||
List<T> list = Lists.newArrayList();
|
||||
for (var override : overrides) {
|
||||
Object o = override.getRight().apply(accessor);
|
||||
if (o != null) {
|
||||
list.addAll(override.getLeft().get(o));
|
||||
}
|
||||
}
|
||||
list.addAll(get(accessor.getTarget()));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Class<?> clazz, T provider) {
|
||||
for (var override : overrides) {
|
||||
if (override.getLeft().isClassAcceptable(clazz)) {
|
||||
override.getLeft().register(clazz, provider);
|
||||
empty = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.register(clazz, provider);
|
||||
empty = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClassAcceptable(Class<?> clazz) {
|
||||
for (var override : overrides) {
|
||||
if (override.getLeft().isClassAcceptable(clazz)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.isClassAcceptable(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
for (var override : overrides) {
|
||||
override.getLeft().invalidate();
|
||||
}
|
||||
super.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadComplete(PriorityStore<ResourceLocation, IJadeProvider> priorityStore) {
|
||||
for (var override : overrides) {
|
||||
override.getLeft().loadComplete(priorityStore);
|
||||
}
|
||||
super.loadComplete(priorityStore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return empty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Map.Entry<Class<?>, Collection<T>>> entries() {
|
||||
Stream<Map.Entry<Class<?>, Collection<T>>> stream = super.entries();
|
||||
for (var override : overrides) {
|
||||
stream = Stream.concat(stream, override.getLeft().entries());
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
@@ -1,390 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.DownloadExchange;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.Exchange;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.ModifyExchangeServer;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.UploadExchange;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.VersionHandshakeServer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@LeavesProtocol(namespace = "syncmatica")
|
||||
public class CommunicationManager {
|
||||
|
||||
private static final Map<UUID, List<ServerPlacement>> downloadingFile = new HashMap<>();
|
||||
private static final Map<ExchangeTarget, ServerPlayer> playerMap = new HashMap<>();
|
||||
|
||||
protected static final Collection<ExchangeTarget> broadcastTargets = new ArrayList<>();
|
||||
|
||||
protected static final Map<UUID, Boolean> downloadState = new HashMap<>();
|
||||
protected static final Map<UUID, Exchange> modifyState = new HashMap<>();
|
||||
|
||||
protected static final Rotation[] rotOrdinals = Rotation.values();
|
||||
protected static final Mirror[] mirOrdinals = Mirror.values();
|
||||
|
||||
public CommunicationManager() {
|
||||
}
|
||||
|
||||
public static boolean shouldEnable() {
|
||||
return org.dreeam.leaf.config.modules.network.ProtocolSupport.syncmaticaProtocol;
|
||||
}
|
||||
|
||||
public static GameProfile getGameProfile(final ExchangeTarget exchangeTarget) {
|
||||
return playerMap.get(exchangeTarget).getGameProfile();
|
||||
}
|
||||
|
||||
public void sendMessage(final @NotNull ExchangeTarget client, final MessageType type, final String identifier) {
|
||||
if (client.getFeatureSet().hasFeature(Feature.MESSAGE)) {
|
||||
final FriendlyByteBuf newPacketBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
newPacketBuf.writeUtf(type.toString());
|
||||
newPacketBuf.writeUtf(identifier);
|
||||
client.sendPacket(PacketType.MESSAGE.identifier, newPacketBuf);
|
||||
} else if (playerMap.containsKey(client)) {
|
||||
final ServerPlayer player = playerMap.get(client);
|
||||
player.sendSystemMessage(Component.literal("Syncmatica " + type.toString() + " " + identifier));
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerJoin
|
||||
public static void onPlayerJoin(ServerPlayer player) {
|
||||
final ExchangeTarget newPlayer = player.connection.exchangeTarget;
|
||||
final VersionHandshakeServer hi = new VersionHandshakeServer(newPlayer);
|
||||
playerMap.put(newPlayer, player);
|
||||
final GameProfile profile = player.getGameProfile();
|
||||
SyncmaticaProtocol.getPlayerIdentifierProvider().updateName(profile.getId(), profile.getName());
|
||||
startExchangeUnchecked(hi);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLeave(ServerPlayer player) {
|
||||
final ExchangeTarget oldPlayer = player.connection.exchangeTarget;
|
||||
final Collection<Exchange> potentialMessageTarget = oldPlayer.getExchanges();
|
||||
if (potentialMessageTarget != null) {
|
||||
for (final Exchange target : potentialMessageTarget) {
|
||||
target.close(false);
|
||||
handleExchange(target);
|
||||
}
|
||||
}
|
||||
broadcastTargets.remove(oldPlayer);
|
||||
playerMap.remove(oldPlayer);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = SyncmaticaPayload.class, payloadId = "main")
|
||||
public static void onPacketGet(ServerPlayer player, SyncmaticaPayload payload) {
|
||||
onPacket(player.connection.exchangeTarget, payload.packetType(), payload.data());
|
||||
}
|
||||
|
||||
public static void onPacket(final @NotNull ExchangeTarget source, final ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
Exchange handler = null;
|
||||
final Collection<Exchange> potentialMessageTarget = source.getExchanges();
|
||||
if (potentialMessageTarget != null) {
|
||||
for (final Exchange target : potentialMessageTarget) {
|
||||
if (target.checkPacket(id, packetBuf)) {
|
||||
target.handle(id, packetBuf);
|
||||
handler = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handler == null) {
|
||||
handle(source, id, packetBuf);
|
||||
} else if (handler.isFinished()) {
|
||||
notifyClose(handler);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void handle(ExchangeTarget source, @NotNull ResourceLocation id, FriendlyByteBuf packetBuf) {
|
||||
if (id.equals(PacketType.REQUEST_LITEMATIC.identifier)) {
|
||||
final UUID syncmaticaId = packetBuf.readUUID();
|
||||
final ServerPlacement placement = SyncmaticaProtocol.getSyncmaticManager().getPlacement(syncmaticaId);
|
||||
if (placement == null) {
|
||||
return;
|
||||
}
|
||||
final File toUpload = SyncmaticaProtocol.getFileStorage().getLocalLitematic(placement);
|
||||
final UploadExchange upload;
|
||||
try {
|
||||
upload = new UploadExchange(placement, toUpload, source);
|
||||
} catch (final FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
startExchange(upload);
|
||||
return;
|
||||
}
|
||||
if (id.equals(PacketType.REGISTER_METADATA.identifier)) {
|
||||
final ServerPlacement placement = receiveMetaData(packetBuf, source);
|
||||
if (SyncmaticaProtocol.getSyncmaticManager().getPlacement(placement.getId()) != null) {
|
||||
cancelShare(source, placement);
|
||||
return;
|
||||
}
|
||||
|
||||
final GameProfile profile = playerMap.get(source).getGameProfile();
|
||||
final PlayerIdentifier playerIdentifier = SyncmaticaProtocol.getPlayerIdentifierProvider().createOrGet(profile);
|
||||
if (!placement.getOwner().equals(playerIdentifier)) {
|
||||
placement.setOwner(playerIdentifier);
|
||||
placement.setLastModifiedBy(playerIdentifier);
|
||||
}
|
||||
|
||||
if (!SyncmaticaProtocol.getFileStorage().getLocalState(placement).isLocalFileReady()) {
|
||||
if (SyncmaticaProtocol.getFileStorage().getLocalState(placement) == LocalLitematicState.DOWNLOADING_LITEMATIC) {
|
||||
downloadingFile.computeIfAbsent(placement.getHash(), key -> new ArrayList<>()).add(placement);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
download(placement, source);
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
addPlacement(source, placement);
|
||||
return;
|
||||
}
|
||||
if (id.equals(PacketType.REMOVE_SYNCMATIC.identifier)) {
|
||||
final UUID placementId = packetBuf.readUUID();
|
||||
final ServerPlacement placement = SyncmaticaProtocol.getSyncmaticManager().getPlacement(placementId);
|
||||
if (placement != null) {
|
||||
if (!getGameProfile(source).getId().equals(placement.getOwner().uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Exchange modifier = getModifier(placement);
|
||||
if (modifier != null) {
|
||||
modifier.close(true);
|
||||
notifyClose(modifier);
|
||||
}
|
||||
SyncmaticaProtocol.getSyncmaticManager().removePlacement(placement);
|
||||
for (final ExchangeTarget client : broadcastTargets) {
|
||||
final FriendlyByteBuf newPacketBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
newPacketBuf.writeUUID(placement.getId());
|
||||
client.sendPacket(PacketType.REMOVE_SYNCMATIC.identifier, newPacketBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id.equals(PacketType.MODIFY_REQUEST.identifier)) {
|
||||
final UUID placementId = packetBuf.readUUID();
|
||||
final ModifyExchangeServer modifier = new ModifyExchangeServer(placementId, source);
|
||||
startExchange(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void handleExchange(Exchange exchange) {
|
||||
if (exchange instanceof DownloadExchange) {
|
||||
final ServerPlacement p = ((DownloadExchange) exchange).getPlacement();
|
||||
|
||||
if (exchange.isSuccessful()) {
|
||||
addPlacement(exchange.getPartner(), p);
|
||||
if (downloadingFile.containsKey(p.getHash())) {
|
||||
for (final ServerPlacement placement : downloadingFile.get(p.getHash())) {
|
||||
addPlacement(exchange.getPartner(), placement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cancelShare(exchange.getPartner(), p);
|
||||
if (downloadingFile.containsKey(p.getHash())) {
|
||||
for (final ServerPlacement placement : downloadingFile.get(p.getHash())) {
|
||||
cancelShare(exchange.getPartner(), placement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadingFile.remove(p.getHash());
|
||||
return;
|
||||
}
|
||||
if (exchange instanceof VersionHandshakeServer && exchange.isSuccessful()) {
|
||||
broadcastTargets.add(exchange.getPartner());
|
||||
}
|
||||
if (exchange instanceof ModifyExchangeServer && exchange.isSuccessful()) {
|
||||
final ServerPlacement placement = ((ModifyExchangeServer) exchange).getPlacement();
|
||||
for (final ExchangeTarget client : broadcastTargets) {
|
||||
if (client.getFeatureSet().hasFeature(Feature.MODIFY)) {
|
||||
final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeUUID(placement.getId());
|
||||
putPositionData(placement, buf, client);
|
||||
if (client.getFeatureSet().hasFeature(Feature.CORE_EX)) {
|
||||
buf.writeUUID(placement.getLastModifiedBy().uuid);
|
||||
buf.writeUtf(placement.getLastModifiedBy().getName());
|
||||
}
|
||||
client.sendPacket(PacketType.MODIFY.identifier, buf);
|
||||
} else {
|
||||
final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeUUID(placement.getId());
|
||||
client.sendPacket(PacketType.REMOVE_SYNCMATIC.identifier, buf);
|
||||
sendMetaData(placement, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addPlacement(final ExchangeTarget t, final @NotNull ServerPlacement placement) {
|
||||
if (SyncmaticaProtocol.getSyncmaticManager().getPlacement(placement.getId()) != null) {
|
||||
cancelShare(t, placement);
|
||||
return;
|
||||
}
|
||||
SyncmaticaProtocol.getSyncmaticManager().addPlacement(placement);
|
||||
for (final ExchangeTarget target : broadcastTargets) {
|
||||
sendMetaData(placement, target);
|
||||
}
|
||||
}
|
||||
|
||||
private static void cancelShare(final @NotNull ExchangeTarget source, final @NotNull ServerPlacement placement) {
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(placement.getId());
|
||||
source.sendPacket(PacketType.CANCEL_SHARE.identifier, FriendlyByteBuf);
|
||||
}
|
||||
|
||||
public static void sendMetaData(final ServerPlacement metaData, final ExchangeTarget target) {
|
||||
final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
putMetaData(metaData, buf, target);
|
||||
target.sendPacket(PacketType.REGISTER_METADATA.identifier, buf);
|
||||
}
|
||||
|
||||
public static void putMetaData(final @NotNull ServerPlacement metaData, final @NotNull FriendlyByteBuf buf, final @NotNull ExchangeTarget exchangeTarget) {
|
||||
buf.writeUUID(metaData.getId());
|
||||
|
||||
buf.writeUtf(SyncmaticaProtocol.sanitizeFileName(metaData.getName()));
|
||||
buf.writeUUID(metaData.getHash());
|
||||
|
||||
if (exchangeTarget.getFeatureSet().hasFeature(Feature.CORE_EX)) {
|
||||
buf.writeUUID(metaData.getOwner().uuid);
|
||||
buf.writeUtf(metaData.getOwner().getName());
|
||||
buf.writeUUID(metaData.getLastModifiedBy().uuid);
|
||||
buf.writeUtf(metaData.getLastModifiedBy().getName());
|
||||
}
|
||||
|
||||
putPositionData(metaData, buf, exchangeTarget);
|
||||
}
|
||||
|
||||
public static void putPositionData(final @NotNull ServerPlacement metaData, final @NotNull FriendlyByteBuf buf, final @NotNull ExchangeTarget exchangeTarget) {
|
||||
buf.writeBlockPos(metaData.getPosition());
|
||||
buf.writeUtf(metaData.getDimension());
|
||||
buf.writeInt(metaData.getRotation().ordinal());
|
||||
buf.writeInt(metaData.getMirror().ordinal());
|
||||
|
||||
if (exchangeTarget.getFeatureSet().hasFeature(Feature.CORE_EX)) {
|
||||
if (metaData.getSubRegionData().getModificationData() == null) {
|
||||
buf.writeInt(0);
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<SubRegionPlacementModification> regionData = metaData.getSubRegionData().getModificationData().values();
|
||||
buf.writeInt(regionData.size());
|
||||
|
||||
for (final SubRegionPlacementModification subPlacement : regionData) {
|
||||
buf.writeUtf(subPlacement.name);
|
||||
buf.writeBlockPos(subPlacement.position);
|
||||
buf.writeInt(subPlacement.rotation.ordinal());
|
||||
buf.writeInt(subPlacement.mirror.ordinal());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ServerPlacement receiveMetaData(final @NotNull FriendlyByteBuf buf, final @NotNull ExchangeTarget exchangeTarget) {
|
||||
final UUID id = buf.readUUID();
|
||||
|
||||
final String fileName = SyncmaticaProtocol.sanitizeFileName(buf.readUtf(32767));
|
||||
final UUID hash = buf.readUUID();
|
||||
|
||||
PlayerIdentifier owner = PlayerIdentifier.MISSING_PLAYER;
|
||||
PlayerIdentifier lastModifiedBy = PlayerIdentifier.MISSING_PLAYER;
|
||||
|
||||
if (exchangeTarget.getFeatureSet().hasFeature(Feature.CORE_EX)) {
|
||||
final PlayerIdentifierProvider provider = SyncmaticaProtocol.getPlayerIdentifierProvider();
|
||||
owner = provider.createOrGet(buf.readUUID(), buf.readUtf(32767));
|
||||
lastModifiedBy = provider.createOrGet(buf.readUUID(), buf.readUtf(32767));
|
||||
}
|
||||
|
||||
final ServerPlacement placement = new ServerPlacement(id, fileName, hash, owner);
|
||||
placement.setLastModifiedBy(lastModifiedBy);
|
||||
|
||||
receivePositionData(placement, buf, exchangeTarget);
|
||||
|
||||
return placement;
|
||||
}
|
||||
|
||||
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()];
|
||||
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();
|
||||
for (int i = 0; i < limit; i++) {
|
||||
subRegionData.modify(buf.readUtf(32767), buf.readBlockPos(), rotOrdinals[buf.readInt()], mirOrdinals[buf.readInt()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
final File toDownload = SyncmaticaProtocol.getFileStorage().createLocalLitematic(syncmatic);
|
||||
final Exchange downloadExchange = new DownloadExchange(syncmatic, toDownload, source);
|
||||
setDownloadState(syncmatic, true);
|
||||
startExchange(downloadExchange);
|
||||
}
|
||||
|
||||
public static void setDownloadState(final @NotNull ServerPlacement syncmatic, final boolean b) {
|
||||
downloadState.put(syncmatic.getHash(), b);
|
||||
}
|
||||
|
||||
public static boolean getDownloadState(final @NotNull ServerPlacement syncmatic) {
|
||||
return downloadState.getOrDefault(syncmatic.getHash(), false);
|
||||
}
|
||||
|
||||
public static void setModifier(final @NotNull ServerPlacement syncmatic, final Exchange exchange) {
|
||||
modifyState.put(syncmatic.getHash(), exchange);
|
||||
}
|
||||
|
||||
public static Exchange getModifier(final @NotNull ServerPlacement syncmatic) {
|
||||
return modifyState.get(syncmatic.getHash());
|
||||
}
|
||||
|
||||
public static void startExchange(final @NotNull Exchange newExchange) {
|
||||
if (!broadcastTargets.contains(newExchange.getPartner())) {
|
||||
throw new IllegalArgumentException(newExchange.getPartner().toString() + " is not a valid ExchangeTarget");
|
||||
}
|
||||
startExchangeUnchecked(newExchange);
|
||||
}
|
||||
|
||||
protected static void startExchangeUnchecked(final @NotNull Exchange newExchange) {
|
||||
newExchange.getPartner().getExchanges().add(newExchange);
|
||||
newExchange.init();
|
||||
if (newExchange.isFinished()) {
|
||||
notifyClose(newExchange);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyClose(final @NotNull Exchange e) {
|
||||
e.getPartner().getExchanges().remove(e);
|
||||
handleExchange(e);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum Feature {
|
||||
CORE,
|
||||
FEATURE,
|
||||
MODIFY,
|
||||
MESSAGE,
|
||||
QUOTA,
|
||||
DEBUG,
|
||||
CORE_EX;
|
||||
|
||||
@Nullable
|
||||
public static Feature fromString(final String s) {
|
||||
for (final Feature f : Feature.values()) {
|
||||
if (f.toString().equals(s)) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FeatureSet {
|
||||
|
||||
private static final Map<String, FeatureSet> versionFeatures;
|
||||
private final Collection<Feature> features;
|
||||
|
||||
@Nullable
|
||||
public static FeatureSet fromVersionString(@NotNull String version) {
|
||||
if (version.matches("^\\d+(\\.\\d+){2,4}$")) {
|
||||
final int minSize = version.indexOf(".");
|
||||
while (version.length() > minSize) {
|
||||
if (versionFeatures.containsKey(version)) {
|
||||
return versionFeatures.get(version);
|
||||
}
|
||||
final int lastDot = version.lastIndexOf(".");
|
||||
version = version.substring(0, lastDot);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FeatureSet fromString(final @NotNull String features) {
|
||||
final FeatureSet featureSet = new FeatureSet(new ArrayList<>());
|
||||
for (final String feature : features.split("\n")) {
|
||||
final Feature f = Feature.fromString(feature);
|
||||
if (f != null) {
|
||||
featureSet.features.add(f);
|
||||
}
|
||||
}
|
||||
return featureSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder output = new StringBuilder();
|
||||
boolean b = false;
|
||||
for (final Feature feature : features) {
|
||||
output.append(b ? "\n" + feature.toString() : feature.toString());
|
||||
b = true;
|
||||
}
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
public FeatureSet(final Collection<Feature> features) {
|
||||
this.features = features;
|
||||
}
|
||||
|
||||
public boolean hasFeature(final Feature f) {
|
||||
return features.contains(f);
|
||||
}
|
||||
|
||||
static {
|
||||
versionFeatures = new HashMap<>();
|
||||
versionFeatures.put("0.1", new FeatureSet(Collections.singletonList(Feature.CORE)));
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FileStorage {
|
||||
|
||||
private final HashMap<ServerPlacement, Long> buffer = new HashMap<>();
|
||||
|
||||
public LocalLitematicState getLocalState(final ServerPlacement placement) {
|
||||
final File localFile = getSchematicPath(placement);
|
||||
if (localFile.isFile()) {
|
||||
if (isDownloading(placement)) {
|
||||
return LocalLitematicState.DOWNLOADING_LITEMATIC;
|
||||
}
|
||||
if ((buffer.containsKey(placement) && buffer.get(placement) == localFile.lastModified()) || hashCompare(localFile, placement)) {
|
||||
return LocalLitematicState.LOCAL_LITEMATIC_PRESENT;
|
||||
}
|
||||
return LocalLitematicState.LOCAL_LITEMATIC_DESYNC;
|
||||
}
|
||||
return LocalLitematicState.NO_LOCAL_LITEMATIC;
|
||||
}
|
||||
|
||||
private boolean isDownloading(final ServerPlacement placement) {
|
||||
return CommunicationManager.getDownloadState(placement);
|
||||
}
|
||||
|
||||
public File getLocalLitematic(final ServerPlacement placement) {
|
||||
if (getLocalState(placement).isLocalFileReady()) {
|
||||
return getSchematicPath(placement);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public File createLocalLitematic(final ServerPlacement placement) {
|
||||
if (getLocalState(placement).isLocalFileReady()) {
|
||||
throw new IllegalArgumentException("");
|
||||
}
|
||||
final File file = getSchematicPath(placement);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private boolean hashCompare(final File localFile, final ServerPlacement placement) {
|
||||
UUID hash = null;
|
||||
try {
|
||||
hash = SyncmaticaProtocol.createChecksum(new FileInputStream(localFile));
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (hash == null) {
|
||||
return false;
|
||||
}
|
||||
if (hash.equals(placement.getHash())) {
|
||||
buffer.put(placement, localFile.lastModified());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
private @NotNull File getSchematicPath(final @NotNull ServerPlacement placement) {
|
||||
return new File(SyncmaticaProtocol.getLitematicFolder(), placement.getHash().toString() + ".litematic");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
public enum LocalLitematicState {
|
||||
NO_LOCAL_LITEMATIC(true, false),
|
||||
LOCAL_LITEMATIC_DESYNC(true, false),
|
||||
DOWNLOADING_LITEMATIC(false, false),
|
||||
LOCAL_LITEMATIC_PRESENT(false, true);
|
||||
|
||||
private final boolean downloadReady;
|
||||
private final boolean fileReady;
|
||||
|
||||
LocalLitematicState(final boolean downloadReady, final boolean fileReady) {
|
||||
this.downloadReady = downloadReady;
|
||||
this.fileReady = fileReady;
|
||||
}
|
||||
|
||||
public boolean isReadyForDownload() {
|
||||
return downloadReady;
|
||||
}
|
||||
|
||||
public boolean isLocalFileReady() {
|
||||
return fileReady;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
public enum MessageType {
|
||||
SUCCESS,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public enum PacketType {
|
||||
REGISTER_METADATA("register_metadata"),
|
||||
CANCEL_SHARE("cancel_share"),
|
||||
REQUEST_LITEMATIC("request_download"),
|
||||
SEND_LITEMATIC("send_litematic"),
|
||||
RECEIVED_LITEMATIC("received_litematic"),
|
||||
FINISHED_LITEMATIC("finished_litematic"),
|
||||
CANCEL_LITEMATIC("cancel_litematic"),
|
||||
REMOVE_SYNCMATIC("remove_syncmatic"),
|
||||
REGISTER_VERSION("register_version"),
|
||||
CONFIRM_USER("confirm_user"),
|
||||
FEATURE_REQUEST("feature_request"),
|
||||
FEATURE("feature"),
|
||||
MODIFY("modify"),
|
||||
MODIFY_REQUEST("modify_request"),
|
||||
MODIFY_REQUEST_DENY("modify_request_deny"),
|
||||
MODIFY_REQUEST_ACCEPT("modify_request_accept"),
|
||||
MODIFY_FINISH("modify_finish"),
|
||||
MESSAGE("mesage");
|
||||
|
||||
public final ResourceLocation identifier;
|
||||
|
||||
PacketType(final String id) {
|
||||
identifier = ResourceLocation.tryBuild(SyncmaticaProtocol.PROTOCOL_ID, id);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerIdentifier {
|
||||
|
||||
public static final UUID MISSING_PLAYER_UUID = UUID.fromString("4c1b738f-56fa-4011-8273-498c972424ea");
|
||||
public static final PlayerIdentifier MISSING_PLAYER = new PlayerIdentifier(MISSING_PLAYER_UUID, "No Player");
|
||||
|
||||
public final UUID uuid;
|
||||
private String bufferedPlayerName;
|
||||
|
||||
PlayerIdentifier(final UUID uuid, final String bufferedPlayerName) {
|
||||
this.uuid = uuid;
|
||||
this.bufferedPlayerName = bufferedPlayerName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return bufferedPlayerName;
|
||||
}
|
||||
|
||||
public void updatePlayerName(final String name) {
|
||||
bufferedPlayerName = name;
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
final JsonObject jsonObject = new JsonObject();
|
||||
|
||||
jsonObject.add("uuid", new JsonPrimitive(uuid.toString()));
|
||||
jsonObject.add("name", new JsonPrimitive(bufferedPlayerName));
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerIdentifierProvider {
|
||||
|
||||
private final Map<UUID, PlayerIdentifier> identifiers = new HashMap<>();
|
||||
|
||||
public PlayerIdentifierProvider() {
|
||||
identifiers.put(PlayerIdentifier.MISSING_PLAYER_UUID, PlayerIdentifier.MISSING_PLAYER);
|
||||
}
|
||||
|
||||
public PlayerIdentifier createOrGet(final ExchangeTarget exchangeTarget) {
|
||||
return createOrGet(CommunicationManager.getGameProfile(exchangeTarget));
|
||||
}
|
||||
|
||||
public PlayerIdentifier createOrGet(final @NotNull GameProfile gameProfile) {
|
||||
return createOrGet(gameProfile.getId(), gameProfile.getName());
|
||||
}
|
||||
|
||||
public PlayerIdentifier createOrGet(final UUID uuid, final String playerName) {
|
||||
return identifiers.computeIfAbsent(uuid, id -> new PlayerIdentifier(uuid, playerName));
|
||||
}
|
||||
|
||||
public void updateName(final UUID uuid, final String playerName) {
|
||||
createOrGet(uuid, playerName).updatePlayerName(playerName);
|
||||
}
|
||||
|
||||
public PlayerIdentifier fromJson(final @NotNull JsonObject obj) {
|
||||
if (!obj.has("uuid") || !obj.has("name")) {
|
||||
return PlayerIdentifier.MISSING_PLAYER;
|
||||
}
|
||||
|
||||
final UUID jsonUUID = UUID.fromString(obj.get("uuid").getAsString());
|
||||
return identifiers.computeIfAbsent(jsonUUID,
|
||||
key -> new PlayerIdentifier(jsonUUID, obj.get("name").getAsString())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ServerPlacement {
|
||||
|
||||
private final UUID id;
|
||||
|
||||
private final String fileName;
|
||||
private final UUID hashValue;
|
||||
|
||||
private PlayerIdentifier owner;
|
||||
private PlayerIdentifier lastModifiedBy;
|
||||
|
||||
private ServerPosition origin;
|
||||
private Rotation rotation;
|
||||
private Mirror mirror;
|
||||
|
||||
private SubRegionData subRegionData = new SubRegionData();
|
||||
|
||||
public ServerPlacement(final UUID id, final String fileName, final UUID hashValue, final PlayerIdentifier owner) {
|
||||
this.id = id;
|
||||
this.fileName = fileName;
|
||||
this.hashValue = hashValue;
|
||||
this.owner = owner;
|
||||
lastModifiedBy = owner;
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public UUID getHash() {
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
public String getDimension() {
|
||||
return origin.getDimensionId();
|
||||
}
|
||||
|
||||
public BlockPos getPosition() {
|
||||
return origin.getBlockPosition();
|
||||
}
|
||||
|
||||
public ServerPosition getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public Rotation getRotation() {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public Mirror getMirror() {
|
||||
return mirror;
|
||||
}
|
||||
|
||||
public ServerPlacement move(final String dimensionId, final BlockPos origin, final Rotation rotation, final Mirror mirror) {
|
||||
move(new ServerPosition(origin, dimensionId), rotation, mirror);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServerPlacement move(final ServerPosition origin, final Rotation rotation, final Mirror mirror) {
|
||||
this.origin = origin;
|
||||
this.rotation = rotation;
|
||||
this.mirror = mirror;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlayerIdentifier getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(final PlayerIdentifier playerIdentifier) {
|
||||
owner = playerIdentifier;
|
||||
}
|
||||
|
||||
public PlayerIdentifier getLastModifiedBy() {
|
||||
return lastModifiedBy;
|
||||
}
|
||||
|
||||
public void setLastModifiedBy(final PlayerIdentifier lastModifiedBy) {
|
||||
this.lastModifiedBy = lastModifiedBy;
|
||||
}
|
||||
|
||||
public SubRegionData getSubRegionData() {
|
||||
return subRegionData;
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
final JsonObject obj = new JsonObject();
|
||||
obj.add("id", new JsonPrimitive(id.toString()));
|
||||
|
||||
obj.add("file_name", new JsonPrimitive(fileName));
|
||||
obj.add("hash", new JsonPrimitive(hashValue.toString()));
|
||||
|
||||
obj.add("origin", origin.toJson());
|
||||
obj.add("rotation", new JsonPrimitive(rotation.name()));
|
||||
obj.add("mirror", new JsonPrimitive(mirror.name()));
|
||||
|
||||
obj.add("owner", owner.toJson());
|
||||
if (!owner.equals(lastModifiedBy)) {
|
||||
obj.add("lastModifiedBy", lastModifiedBy.toJson());
|
||||
}
|
||||
|
||||
if (subRegionData.isModified()) {
|
||||
obj.add("subregionData", subRegionData.toJson());
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ServerPlacement fromJson(final @NotNull JsonObject obj) {
|
||||
if (obj.has("id")
|
||||
&& obj.has("file_name")
|
||||
&& obj.has("hash")
|
||||
&& obj.has("origin")
|
||||
&& obj.has("rotation")
|
||||
&& obj.has("mirror")) {
|
||||
final UUID id = UUID.fromString(obj.get("id").getAsString());
|
||||
final String name = obj.get("file_name").getAsString();
|
||||
final UUID hashValue = UUID.fromString(obj.get("hash").getAsString());
|
||||
|
||||
PlayerIdentifier owner = PlayerIdentifier.MISSING_PLAYER;
|
||||
if (obj.has("owner")) {
|
||||
owner = SyncmaticaProtocol.getPlayerIdentifierProvider().fromJson(obj.get("owner").getAsJsonObject());
|
||||
}
|
||||
|
||||
final ServerPlacement newPlacement = new ServerPlacement(id, name, hashValue, owner);
|
||||
final ServerPosition pos = ServerPosition.fromJson(obj.get("origin").getAsJsonObject());
|
||||
if (pos == null) {
|
||||
return null;
|
||||
}
|
||||
newPlacement.origin = pos;
|
||||
newPlacement.rotation = Rotation.valueOf(obj.get("rotation").getAsString());
|
||||
newPlacement.mirror = Mirror.valueOf(obj.get("mirror").getAsString());
|
||||
|
||||
if (obj.has("lastModifiedBy")) {
|
||||
newPlacement.lastModifiedBy = SyncmaticaProtocol.getPlayerIdentifierProvider()
|
||||
.fromJson(obj.get("lastModifiedBy").getAsJsonObject());
|
||||
} else {
|
||||
newPlacement.lastModifiedBy = owner;
|
||||
}
|
||||
|
||||
if (obj.has("subregionData")) {
|
||||
newPlacement.subRegionData = SubRegionData.fromJson(obj.get("subregionData"));
|
||||
}
|
||||
|
||||
return newPlacement;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
public class ServerPosition {
|
||||
|
||||
private final BlockPos position;
|
||||
private final String dimensionId;
|
||||
|
||||
public ServerPosition(final BlockPos pos, final String dim) {
|
||||
position = pos;
|
||||
dimensionId = dim;
|
||||
}
|
||||
|
||||
public BlockPos getBlockPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public String getDimensionId() {
|
||||
return dimensionId;
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
final JsonObject obj = new JsonObject();
|
||||
final JsonArray arr = new JsonArray();
|
||||
arr.add(new JsonPrimitive(position.getX()));
|
||||
arr.add(new JsonPrimitive(position.getY()));
|
||||
arr.add(new JsonPrimitive(position.getZ()));
|
||||
obj.add("position", arr);
|
||||
obj.add("dimension", new JsonPrimitive(dimensionId));
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static ServerPosition fromJson(final JsonObject obj) {
|
||||
if (obj.has("position") && obj.has("dimension")) {
|
||||
final int x;
|
||||
final int y;
|
||||
final int z;
|
||||
final JsonArray arr = obj.get("position").getAsJsonArray();
|
||||
x = arr.get(0).getAsInt();
|
||||
y = arr.get(1).getAsInt();
|
||||
z = arr.get(2).getAsInt();
|
||||
final BlockPos pos = new BlockPos(x, y, z);
|
||||
return new ServerPosition(pos, obj.get("dimension").getAsString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SubRegionData {
|
||||
|
||||
private boolean isModified;
|
||||
private Map<String, SubRegionPlacementModification> modificationData; // is null when isModified is false
|
||||
|
||||
public SubRegionData() {
|
||||
this(false, null);
|
||||
}
|
||||
|
||||
public SubRegionData(final boolean isModified, final Map<String, SubRegionPlacementModification> modificationData) {
|
||||
this.isModified = isModified;
|
||||
this.modificationData = modificationData;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
isModified = false;
|
||||
modificationData = null;
|
||||
}
|
||||
|
||||
public void modify(final String name, final BlockPos position, final Rotation rotation, final Mirror mirror) {
|
||||
modify(new SubRegionPlacementModification(name, position, rotation, mirror));
|
||||
}
|
||||
|
||||
public void modify(final SubRegionPlacementModification subRegionPlacementModification) {
|
||||
if (subRegionPlacementModification == null) {
|
||||
return;
|
||||
}
|
||||
isModified = true;
|
||||
if (modificationData == null) {
|
||||
modificationData = new HashMap<>();
|
||||
}
|
||||
modificationData.put(subRegionPlacementModification.name, subRegionPlacementModification);
|
||||
}
|
||||
|
||||
public boolean isModified() {
|
||||
return isModified;
|
||||
}
|
||||
|
||||
public Map<String, SubRegionPlacementModification> getModificationData() {
|
||||
return modificationData;
|
||||
}
|
||||
|
||||
public JsonElement toJson() {
|
||||
return modificationDataToJson();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsonElement modificationDataToJson() {
|
||||
final JsonArray arr = new JsonArray();
|
||||
|
||||
for (final Map.Entry<String, SubRegionPlacementModification> entry : modificationData.entrySet()) {
|
||||
arr.add(entry.getValue().toJson());
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static SubRegionData fromJson(final @NotNull JsonElement obj) {
|
||||
final SubRegionData newSubRegionData = new SubRegionData();
|
||||
|
||||
newSubRegionData.isModified = true;
|
||||
|
||||
for (final JsonElement modification : obj.getAsJsonArray()) {
|
||||
newSubRegionData.modify(SubRegionPlacementModification.fromJson(modification.getAsJsonObject()));
|
||||
}
|
||||
|
||||
return newSubRegionData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!isModified) {
|
||||
return "[]";
|
||||
}
|
||||
return modificationData == null ? "[ERROR:null]" : modificationData.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SubRegionPlacementModification {
|
||||
|
||||
public final String name;
|
||||
public final BlockPos position;
|
||||
public final Rotation rotation;
|
||||
public final Mirror mirror;
|
||||
|
||||
SubRegionPlacementModification(final String name, final BlockPos position, final Rotation rotation, final Mirror mirror) {
|
||||
this.name = name;
|
||||
this.position = position;
|
||||
this.rotation = rotation;
|
||||
this.mirror = mirror;
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
final JsonObject obj = new JsonObject();
|
||||
|
||||
final JsonArray arr = new JsonArray();
|
||||
arr.add(position.getX());
|
||||
arr.add(position.getY());
|
||||
arr.add(position.getZ());
|
||||
obj.add("position", arr);
|
||||
|
||||
obj.add("name", new JsonPrimitive(name));
|
||||
obj.add("rotation", new JsonPrimitive(rotation.name()));
|
||||
obj.add("mirror", new JsonPrimitive(mirror.name()));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SubRegionPlacementModification fromJson(final @NotNull JsonObject obj) {
|
||||
if (!obj.has("name") || !obj.has("position") || !obj.has("rotation") || !obj.has("mirror")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String name = obj.get("name").getAsString();
|
||||
final JsonArray arr = obj.get("position").getAsJsonArray();
|
||||
if (arr.size() != 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final BlockPos position = new BlockPos(arr.get(0).getAsInt(), arr.get(1).getAsInt(), arr.get(2).getAsInt());
|
||||
final Rotation rotation = Rotation.valueOf(obj.get("rotation").getAsString());
|
||||
final Mirror mirror = Mirror.valueOf(obj.get("mirror").getAsString());
|
||||
|
||||
return new SubRegionPlacementModification(name, position, rotation, mirror);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[name=%s, position=%s, rotation=%s, mirror=%s]", name, position, rotation, mirror);
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SyncmaticManager {
|
||||
|
||||
public static final String PLACEMENTS_JSON_KEY = "placements";
|
||||
private final Map<UUID, ServerPlacement> schematics = new HashMap<>();
|
||||
|
||||
public void addPlacement(final ServerPlacement placement) {
|
||||
schematics.put(placement.getId(), placement);
|
||||
updateServerPlacement();
|
||||
}
|
||||
|
||||
public ServerPlacement getPlacement(final UUID id) {
|
||||
return schematics.get(id);
|
||||
}
|
||||
|
||||
public Collection<ServerPlacement> getAll() {
|
||||
return schematics.values();
|
||||
}
|
||||
|
||||
public void removePlacement(final @NotNull ServerPlacement placement) {
|
||||
schematics.remove(placement.getId());
|
||||
updateServerPlacement();
|
||||
}
|
||||
|
||||
public void updateServerPlacement() {
|
||||
saveServer();
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
loadServer();
|
||||
}
|
||||
|
||||
private void saveServer() {
|
||||
final JsonObject obj = new JsonObject();
|
||||
final JsonArray arr = new JsonArray();
|
||||
|
||||
for (final ServerPlacement p : getAll()) {
|
||||
arr.add(p.toJson());
|
||||
}
|
||||
|
||||
obj.add(PLACEMENTS_JSON_KEY, arr);
|
||||
final File backup = new File(SyncmaticaProtocol.getLitematicFolder(), "placements.json.bak");
|
||||
final File incoming = new File(SyncmaticaProtocol.getLitematicFolder(), "placements.json.new");
|
||||
final File current = new File(SyncmaticaProtocol.getLitematicFolder(), "placements.json");
|
||||
|
||||
try (final FileWriter writer = new FileWriter(incoming)) {
|
||||
writer.write(new GsonBuilder().setPrettyPrinting().create().toJson(obj));
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
SyncmaticaProtocol.backupAndReplace(backup.toPath(), current.toPath(), incoming.toPath());
|
||||
}
|
||||
|
||||
private void loadServer() {
|
||||
final File f = new File(SyncmaticaProtocol.getLitematicFolder(), "placements.json");
|
||||
if (f.exists() && f.isFile() && f.canRead()) {
|
||||
JsonElement element = null;
|
||||
try {
|
||||
final JsonParser parser = new JsonParser();
|
||||
final FileReader reader = new FileReader(f);
|
||||
|
||||
element = parser.parse(reader);
|
||||
reader.close();
|
||||
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (element == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final JsonObject obj = element.getAsJsonObject();
|
||||
if (obj == null || !obj.has(PLACEMENTS_JSON_KEY)) {
|
||||
return;
|
||||
}
|
||||
final JsonArray arr = obj.getAsJsonArray(PLACEMENTS_JSON_KEY);
|
||||
for (final JsonElement elem : arr) {
|
||||
final ServerPlacement placement = ServerPlacement.fromJson(elem.getAsJsonObject());
|
||||
if (placement != null) {
|
||||
schematics.put(placement.getId(), placement);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final IllegalStateException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
|
||||
public record SyncmaticaPayload(ResourceLocation packetType,
|
||||
FriendlyByteBuf data) implements LeavesCustomPayload<SyncmaticaPayload> {
|
||||
|
||||
private static final ResourceLocation NETWORK_ID = ResourceLocation.tryBuild(SyncmaticaProtocol.PROTOCOL_ID, "main");
|
||||
|
||||
@New
|
||||
public static SyncmaticaPayload decode(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
return new SyncmaticaPayload(buf.readResourceLocation(), new FriendlyByteBuf(buf.readBytes(buf.readableBytes())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
buf.writeResourceLocation(this.packetType);
|
||||
buf.writeBytes(this.data.readBytes(this.data.readableBytes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation id() {
|
||||
return NETWORK_ID;
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SyncmaticaProtocol {
|
||||
|
||||
public static final String PROTOCOL_ID = "syncmatica";
|
||||
public static final String PROTOCOL_VERSION = "leaves-syncmatica-1.1.0";
|
||||
|
||||
private static boolean loaded = false;
|
||||
private static final File litematicFolder = new File("." + File.separator + "syncmatics");
|
||||
private static final PlayerIdentifierProvider playerIdentifierProvider = new PlayerIdentifierProvider();
|
||||
private static final CommunicationManager communicationManager = new CommunicationManager();
|
||||
private static final FeatureSet featureSet = new FeatureSet(Arrays.asList(Feature.values()));
|
||||
private static final SyncmaticManager syncmaticManager = new SyncmaticManager();
|
||||
private static final FileStorage fileStorage = new FileStorage();
|
||||
|
||||
public static File getLitematicFolder() {
|
||||
return litematicFolder;
|
||||
}
|
||||
|
||||
public static PlayerIdentifierProvider getPlayerIdentifierProvider() {
|
||||
return playerIdentifierProvider;
|
||||
}
|
||||
|
||||
public static CommunicationManager getCommunicationManager() {
|
||||
return communicationManager;
|
||||
}
|
||||
|
||||
public static FeatureSet getFeatureSet() {
|
||||
return featureSet;
|
||||
}
|
||||
|
||||
public static SyncmaticManager getSyncmaticManager() {
|
||||
return syncmaticManager;
|
||||
}
|
||||
|
||||
public static FileStorage getFileStorage() {
|
||||
return fileStorage;
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
if (!loaded) {
|
||||
litematicFolder.mkdirs();
|
||||
syncmaticManager.startup();
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static UUID createChecksum(final @NotNull InputStream fis) throws NoSuchAlgorithmException, IOException {
|
||||
final byte[] buffer = new byte[4096];
|
||||
final MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
int numRead;
|
||||
|
||||
do {
|
||||
numRead = fis.read(buffer);
|
||||
if (numRead > 0) {
|
||||
messageDigest.update(buffer, 0, numRead);
|
||||
}
|
||||
} while (numRead != -1);
|
||||
|
||||
fis.close();
|
||||
return UUID.nameUUIDFromBytes(messageDigest.digest());
|
||||
}
|
||||
|
||||
private static final int[] ILLEGAL_CHARS = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};
|
||||
private static final String ILLEGAL_PATTERNS = "(^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\\..*)?$)|(^\\.\\.*$)";
|
||||
|
||||
@NotNull
|
||||
public static String sanitizeFileName(final @NotNull String badFileName) {
|
||||
final StringBuilder sanitized = new StringBuilder();
|
||||
final int len = badFileName.codePointCount(0, badFileName.length());
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
final int c = badFileName.codePointAt(i);
|
||||
if (Arrays.binarySearch(ILLEGAL_CHARS, c) < 0) {
|
||||
sanitized.appendCodePoint(c);
|
||||
if (sanitized.length() == 255) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sanitized.toString().replaceAll(ILLEGAL_PATTERNS, "_");
|
||||
}
|
||||
|
||||
public static boolean isOverQuota(int sent) {
|
||||
return org.dreeam.leaf.config.modules.network.ProtocolSupport.syncmaticaQuota && sent > org.dreeam.leaf.config.modules.network.ProtocolSupport.syncmaticaQuotaLimit;
|
||||
}
|
||||
|
||||
public static void backupAndReplace(final Path backup, final Path current, final Path incoming) {
|
||||
if (!Files.exists(incoming)) {
|
||||
return;
|
||||
}
|
||||
if (overwrite(backup, current, 2) && !overwrite(current, incoming, 4)) {
|
||||
overwrite(current, backup, 8);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean overwrite(final Path backup, final Path current, final int tries) {
|
||||
if (!Files.exists(current)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
Files.deleteIfExists(backup);
|
||||
Files.move(current, backup);
|
||||
} catch (final IOException exception) {
|
||||
if (tries <= 0) {
|
||||
return false;
|
||||
}
|
||||
return overwrite(backup, current, tries - 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class AbstractExchange implements Exchange {
|
||||
|
||||
private boolean success = false;
|
||||
private boolean finished = false;
|
||||
private final ExchangeTarget partner;
|
||||
|
||||
protected AbstractExchange(final ExchangeTarget partner) {
|
||||
this.partner = partner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExchangeTarget getPartner() {
|
||||
return partner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessful() {
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(final boolean notifyPartner) {
|
||||
finished = true;
|
||||
success = false;
|
||||
onClose();
|
||||
if (notifyPartner) {
|
||||
sendCancelPacket();
|
||||
}
|
||||
}
|
||||
|
||||
protected void sendCancelPacket() {
|
||||
}
|
||||
|
||||
protected void onClose() {
|
||||
}
|
||||
|
||||
protected void succeed() {
|
||||
finished = true;
|
||||
success = true;
|
||||
onClose();
|
||||
}
|
||||
|
||||
protected static boolean checkUUID(final FriendlyByteBuf sourceBuf, final UUID targetId) {
|
||||
final int r = sourceBuf.readerIndex();
|
||||
final UUID sourceId = sourceBuf.readUUID();
|
||||
sourceBuf.readerIndex(r);
|
||||
return sourceId.equals(targetId);
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.CommunicationManager;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.MessageType;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.PacketType;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.ServerPlacement;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DownloadExchange extends AbstractExchange {
|
||||
|
||||
private final ServerPlacement toDownload;
|
||||
private final OutputStream outputStream;
|
||||
private final MessageDigest md5;
|
||||
private final File downloadFile;
|
||||
private int bytesSent;
|
||||
|
||||
public DownloadExchange(final ServerPlacement syncmatic, final File downloadFile, final ExchangeTarget partner) throws IOException, NoSuchAlgorithmException {
|
||||
super(partner);
|
||||
this.downloadFile = downloadFile;
|
||||
final OutputStream os = new FileOutputStream(downloadFile);
|
||||
toDownload = syncmatic;
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
outputStream = new DigestOutputStream(os, md5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPacket(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
if (id.equals(PacketType.SEND_LITEMATIC.identifier)
|
||||
|| id.equals(PacketType.FINISHED_LITEMATIC.identifier)
|
||||
|| id.equals(PacketType.CANCEL_LITEMATIC.identifier)) {
|
||||
return checkUUID(packetBuf, toDownload.getId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull ResourceLocation id, final @NotNull FriendlyByteBuf packetBuf) {
|
||||
packetBuf.readUUID();
|
||||
if (id.equals(PacketType.SEND_LITEMATIC.identifier)) {
|
||||
final int size = packetBuf.readInt();
|
||||
bytesSent += size;
|
||||
if (SyncmaticaProtocol.isOverQuota(bytesSent)) {
|
||||
close(true);
|
||||
SyncmaticaProtocol.getCommunicationManager().sendMessage(
|
||||
getPartner(),
|
||||
MessageType.ERROR,
|
||||
"syncmatica.error.cancelled_transmit_exceed_quota"
|
||||
);
|
||||
}
|
||||
try {
|
||||
packetBuf.readBytes(outputStream, size);
|
||||
} catch (final IOException e) {
|
||||
close(true);
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(toDownload.getId());
|
||||
getPartner().sendPacket(PacketType.RECEIVED_LITEMATIC.identifier, FriendlyByteBuf);
|
||||
return;
|
||||
}
|
||||
if (id.equals(PacketType.FINISHED_LITEMATIC.identifier)) {
|
||||
try {
|
||||
outputStream.flush();
|
||||
} catch (final IOException e) {
|
||||
close(false);
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
final UUID downloadHash = UUID.nameUUIDFromBytes(md5.digest());
|
||||
if (downloadHash.equals(toDownload.getHash())) {
|
||||
succeed();
|
||||
} else {
|
||||
close(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (id.equals(PacketType.CANCEL_LITEMATIC.identifier)) {
|
||||
close(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(toDownload.getId());
|
||||
getPartner().sendPacket(PacketType.REQUEST_LITEMATIC.identifier, FriendlyByteBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClose() {
|
||||
CommunicationManager.setDownloadState(toDownload, false);
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!isSuccessful() && downloadFile.exists()) {
|
||||
downloadFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendCancelPacket() {
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(toDownload.getId());
|
||||
getPartner().sendPacket(PacketType.CANCEL_LITEMATIC.identifier, FriendlyByteBuf);
|
||||
}
|
||||
|
||||
public ServerPlacement getPlacement() {
|
||||
return toDownload;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface Exchange {
|
||||
|
||||
ExchangeTarget getPartner();
|
||||
|
||||
boolean checkPacket(ResourceLocation id, FriendlyByteBuf packetBuf);
|
||||
|
||||
void handle(ResourceLocation id, FriendlyByteBuf packetBuf);
|
||||
|
||||
boolean isFinished();
|
||||
|
||||
boolean isSuccessful();
|
||||
|
||||
void close(boolean notifyPartner);
|
||||
|
||||
void init();
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.FeatureSet;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaPayload;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ExchangeTarget {
|
||||
|
||||
private final List<Exchange> ongoingExchanges = new ArrayList<>();
|
||||
private final ServerGamePacketListenerImpl client;
|
||||
private FeatureSet features;
|
||||
|
||||
public ExchangeTarget(final ServerGamePacketListenerImpl client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public void sendPacket(final ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
ProtocolUtils.sendPayloadPacket(client.player, new SyncmaticaPayload(id, packetBuf));
|
||||
}
|
||||
|
||||
public FeatureSet getFeatureSet() {
|
||||
return features;
|
||||
}
|
||||
|
||||
public void setFeatureSet(final FeatureSet f) {
|
||||
features = f;
|
||||
}
|
||||
|
||||
public Collection<Exchange> getExchanges() {
|
||||
return ongoingExchanges;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.FeatureSet;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.PacketType;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol;
|
||||
|
||||
public abstract class FeatureExchange extends AbstractExchange {
|
||||
|
||||
protected FeatureExchange(final ExchangeTarget partner) {
|
||||
super(partner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPacket(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
return id.equals(PacketType.FEATURE_REQUEST.identifier)
|
||||
|| id.equals(PacketType.FEATURE.identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
if (id.equals(PacketType.FEATURE_REQUEST.identifier)) {
|
||||
sendFeatures();
|
||||
} else if (id.equals(PacketType.FEATURE.identifier)) {
|
||||
final FeatureSet fs = FeatureSet.fromString(packetBuf.readUtf(32767));
|
||||
getPartner().setFeatureSet(fs);
|
||||
onFeatureSetReceive();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onFeatureSetReceive() {
|
||||
succeed();
|
||||
}
|
||||
|
||||
public void requestFeatureSet() {
|
||||
getPartner().sendPacket(PacketType.FEATURE_REQUEST.identifier, new FriendlyByteBuf(Unpooled.buffer()));
|
||||
}
|
||||
|
||||
private void sendFeatures() {
|
||||
final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
final FeatureSet fs = SyncmaticaProtocol.getFeatureSet();
|
||||
buf.writeUtf(fs.toString(), 32767);
|
||||
getPartner().sendPacket(PacketType.FEATURE.identifier, buf);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.CommunicationManager;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.PacketType;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.PlayerIdentifier;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.ServerPlacement;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ModifyExchangeServer extends AbstractExchange {
|
||||
|
||||
private final ServerPlacement placement;
|
||||
UUID placementId;
|
||||
|
||||
public ModifyExchangeServer(final UUID placeId, final ExchangeTarget partner) {
|
||||
super(partner);
|
||||
placementId = placeId;
|
||||
placement = SyncmaticaProtocol.getSyncmaticManager().getPlacement(placementId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPacket(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
return id.equals(PacketType.MODIFY_FINISH.identifier) && checkUUID(packetBuf, placement.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull ResourceLocation id, final @NotNull FriendlyByteBuf packetBuf) {
|
||||
packetBuf.readUUID();
|
||||
if (id.equals(PacketType.MODIFY_FINISH.identifier)) {
|
||||
CommunicationManager.receivePositionData(placement, packetBuf, getPartner());
|
||||
final PlayerIdentifier identifier = SyncmaticaProtocol.getPlayerIdentifierProvider().createOrGet(
|
||||
getPartner()
|
||||
);
|
||||
placement.setLastModifiedBy(identifier);
|
||||
SyncmaticaProtocol.getSyncmaticManager().updateServerPlacement();
|
||||
succeed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (getPlacement() == null || CommunicationManager.getModifier(placement) != null) {
|
||||
close(true);
|
||||
} else {
|
||||
if (SyncmaticaProtocol.getPlayerIdentifierProvider().createOrGet(this.getPartner()).uuid.equals(placement.getOwner().uuid)) {
|
||||
accept();
|
||||
} else {
|
||||
close(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void accept() {
|
||||
final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeUUID(placement.getId());
|
||||
getPartner().sendPacket(PacketType.MODIFY_REQUEST_ACCEPT.identifier, buf);
|
||||
CommunicationManager.setModifier(placement, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendCancelPacket() {
|
||||
final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buf.writeUUID(placementId);
|
||||
getPartner().sendPacket(PacketType.MODIFY_REQUEST_DENY.identifier, buf);
|
||||
}
|
||||
|
||||
public ServerPlacement getPlacement() {
|
||||
return placement;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClose() {
|
||||
if (CommunicationManager.getModifier(placement) == this) {
|
||||
CommunicationManager.setModifier(placement, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.PacketType;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.ServerPlacement;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class UploadExchange extends AbstractExchange {
|
||||
|
||||
private static final int BUFFER_SIZE = 16384;
|
||||
|
||||
private final ServerPlacement toUpload;
|
||||
private final InputStream inputStream;
|
||||
private final byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
public UploadExchange(final ServerPlacement syncmatic, final File uploadFile, final ExchangeTarget partner) throws FileNotFoundException {
|
||||
super(partner);
|
||||
toUpload = syncmatic;
|
||||
inputStream = new FileInputStream(uploadFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPacket(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
if (id.equals(PacketType.RECEIVED_LITEMATIC.identifier)
|
||||
|| id.equals(PacketType.CANCEL_LITEMATIC.identifier)) {
|
||||
return checkUUID(packetBuf, toUpload.getId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull ResourceLocation id, final @NotNull FriendlyByteBuf packetBuf) {
|
||||
packetBuf.readUUID();
|
||||
if (id.equals(PacketType.RECEIVED_LITEMATIC.identifier)) {
|
||||
send();
|
||||
}
|
||||
if (id.equals(PacketType.CANCEL_LITEMATIC.identifier)) {
|
||||
close(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void send() {
|
||||
final int bytesRead;
|
||||
try {
|
||||
bytesRead = inputStream.read(buffer);
|
||||
} catch (final IOException e) {
|
||||
close(true);
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
if (bytesRead == -1) {
|
||||
sendFinish();
|
||||
} else {
|
||||
sendData(bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendData(final int bytesRead) {
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(toUpload.getId());
|
||||
FriendlyByteBuf.writeInt(bytesRead);
|
||||
FriendlyByteBuf.writeBytes(buffer, 0, bytesRead);
|
||||
getPartner().sendPacket(PacketType.SEND_LITEMATIC.identifier, FriendlyByteBuf);
|
||||
}
|
||||
|
||||
private void sendFinish() {
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(toUpload.getId());
|
||||
getPartner().sendPacket(PacketType.FINISHED_LITEMATIC.identifier, FriendlyByteBuf);
|
||||
succeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
send();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClose() {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendCancelPacket() {
|
||||
final FriendlyByteBuf FriendlyByteBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
FriendlyByteBuf.writeUUID(toUpload.getId());
|
||||
getPartner().sendPacket(PacketType.CANCEL_LITEMATIC.identifier, FriendlyByteBuf);
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.leavesmc.leaves.protocol.syncmatica.exchange;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.CommunicationManager;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.FeatureSet;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.PacketType;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.ServerPlacement;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class VersionHandshakeServer extends FeatureExchange {
|
||||
|
||||
public VersionHandshakeServer(final ExchangeTarget partner) {
|
||||
super(partner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPacket(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
return id.equals(PacketType.REGISTER_VERSION.identifier)
|
||||
|| super.checkPacket(id, packetBuf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull ResourceLocation id, final FriendlyByteBuf packetBuf) {
|
||||
if (id.equals(PacketType.REGISTER_VERSION.identifier)) {
|
||||
String partnerVersion = packetBuf.readUtf();
|
||||
if (partnerVersion.equals("0.0.1")) {
|
||||
close(false);
|
||||
return;
|
||||
}
|
||||
final FeatureSet fs = FeatureSet.fromVersionString(partnerVersion);
|
||||
if (fs == null) {
|
||||
requestFeatureSet();
|
||||
} else {
|
||||
getPartner().setFeatureSet(fs);
|
||||
onFeatureSetReceive();
|
||||
}
|
||||
} else {
|
||||
super.handle(id, packetBuf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFeatureSetReceive() {
|
||||
final FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
final Collection<ServerPlacement> l = SyncmaticaProtocol.getSyncmaticManager().getAll();
|
||||
newBuf.writeInt(l.size());
|
||||
for (final ServerPlacement p : l) {
|
||||
CommunicationManager.putMetaData(p, newBuf, getPartner());
|
||||
}
|
||||
getPartner().sendPacket(PacketType.CONFIRM_USER.identifier, newBuf);
|
||||
succeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
final FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
newBuf.writeUtf(SyncmaticaProtocol.PROTOCOL_VERSION);
|
||||
getPartner().sendPacket(PacketType.REGISTER_VERSION.identifier, newBuf);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.leavesmc.leaves.replay;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
public class DigestOutputStream extends OutputStream {
|
||||
|
||||
private final Checksum sum;
|
||||
private final OutputStream out;
|
||||
|
||||
public DigestOutputStream(OutputStream out, Checksum sum) {
|
||||
this.out = out;
|
||||
this.sum = sum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
sum.update(b);
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] b) throws IOException {
|
||||
sum.update(b);
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] b, int off, int len) throws IOException {
|
||||
sum.update(b, off, len);
|
||||
out.write(b, off, len);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.leavesmc.leaves.replay;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class RecordMetaData {
|
||||
|
||||
public static final int CURRENT_FILE_FORMAT_VERSION = 14;
|
||||
|
||||
public boolean singleplayer = false;
|
||||
public String serverName = "Leaf";
|
||||
public int duration = 0;
|
||||
public long date;
|
||||
public String mcversion;
|
||||
public String fileFormat = "MCPR";
|
||||
public int fileFormatVersion;
|
||||
public int protocol;
|
||||
public String generator;
|
||||
public int selfId = -1;
|
||||
|
||||
public Set<UUID> players = new HashSet<>();
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
package org.leavesmc.leaves.replay;
|
||||
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import io.netty.channel.local.LocalChannel;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.LayeredRegistryAccess;
|
||||
import net.minecraft.core.RegistrySynchronization;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.ConnectionProtocol;
|
||||
import net.minecraft.network.PacketSendListener;
|
||||
import net.minecraft.network.protocol.BundlePacket;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundDisconnectPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundServerLinksPacket;
|
||||
import net.minecraft.network.protocol.common.ClientboundUpdateTagsPacket;
|
||||
import net.minecraft.network.protocol.common.custom.BrandPayload;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundFinishConfigurationPacket;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundSelectKnownPacks;
|
||||
import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeaturesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetTimePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSystemChatPacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.RegistryLayer;
|
||||
import net.minecraft.server.packs.repository.KnownPack;
|
||||
import net.minecraft.tags.TagNetworkSerialization;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.LeavesLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Recorder extends Connection {
|
||||
|
||||
private static final LeavesLogger LOGGER = LeavesLogger.LOGGER;
|
||||
|
||||
private final ReplayFile replayFile;
|
||||
private final ServerPhotographer photographer;
|
||||
private final RecorderOption recorderOption;
|
||||
private final RecordMetaData metaData;
|
||||
|
||||
private final ExecutorService saveService = Executors.newSingleThreadExecutor();
|
||||
|
||||
private boolean stopped = false;
|
||||
private boolean paused = false;
|
||||
private boolean resumeOnNextPacket = true;
|
||||
|
||||
private long startTime;
|
||||
private long lastPacket;
|
||||
private long timeShift = 0;
|
||||
|
||||
private boolean isSaved;
|
||||
private boolean isSaving;
|
||||
private ConnectionProtocol state = ConnectionProtocol.LOGIN;
|
||||
|
||||
public Recorder(ServerPhotographer photographer, RecorderOption recorderOption, File replayFile) throws IOException {
|
||||
super(PacketFlow.CLIENTBOUND);
|
||||
|
||||
this.photographer = photographer;
|
||||
this.recorderOption = recorderOption;
|
||||
this.metaData = new RecordMetaData();
|
||||
this.replayFile = new ReplayFile(replayFile);
|
||||
this.channel = new LocalChannel();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
startTime = System.currentTimeMillis();
|
||||
|
||||
metaData.singleplayer = false;
|
||||
metaData.serverName = recorderOption.serverName;
|
||||
metaData.date = startTime;
|
||||
metaData.mcversion = SharedConstants.getCurrentVersion().getName();
|
||||
|
||||
// TODO start event
|
||||
this.savePacket(new ClientboundLoginFinishedPacket(photographer.getGameProfile()), ConnectionProtocol.LOGIN);
|
||||
this.startConfiguration();
|
||||
|
||||
if (recorderOption.forceWeather != null) {
|
||||
setWeather(recorderOption.forceWeather);
|
||||
}
|
||||
}
|
||||
|
||||
public void startConfiguration() {
|
||||
this.state = ConnectionProtocol.CONFIGURATION;
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
this.savePacket(new ClientboundCustomPayloadPacket(new BrandPayload(server.getServerModName())), ConnectionProtocol.CONFIGURATION);
|
||||
this.savePacket(new ClientboundServerLinksPacket(server.serverLinks().untrust()), ConnectionProtocol.CONFIGURATION);
|
||||
this.savePacket(new ClientboundUpdateEnabledFeaturesPacket(FeatureFlags.REGISTRY.toNames(server.getWorldData().enabledFeatures())), ConnectionProtocol.CONFIGURATION);
|
||||
|
||||
List<KnownPack> knownPackslist = server.getResourceManager().listPacks().flatMap((iresourcepack) -> iresourcepack.location().knownPackInfo().stream()).toList();
|
||||
this.savePacket(new ClientboundSelectKnownPacks(knownPackslist), ConnectionProtocol.CONFIGURATION);
|
||||
|
||||
server.getServerResourcePack().ifPresent((info) -> this.savePacket(new ClientboundResourcePackPushPacket(
|
||||
info.id(), info.url(), info.hash(), info.isRequired(), Optional.ofNullable(info.prompt())
|
||||
)));
|
||||
|
||||
LayeredRegistryAccess<RegistryLayer> layeredregistryaccess = server.registries();
|
||||
DynamicOps<Tag> dynamicOps = layeredregistryaccess.compositeAccess().createSerializationContext(NbtOps.INSTANCE);
|
||||
RegistrySynchronization.packRegistries(dynamicOps, layeredregistryaccess.getAccessFrom(RegistryLayer.WORLDGEN), Set.copyOf(knownPackslist),
|
||||
(key, entries) ->
|
||||
this.savePacket(new ClientboundRegistryDataPacket(key, entries), ConnectionProtocol.CONFIGURATION)
|
||||
);
|
||||
this.savePacket(new ClientboundUpdateTagsPacket(TagNetworkSerialization.serializeTagsToNetwork(layeredregistryaccess)), ConnectionProtocol.CONFIGURATION);
|
||||
|
||||
this.savePacket(ClientboundFinishConfigurationPacket.INSTANCE, ConnectionProtocol.CONFIGURATION);
|
||||
state = ConnectionProtocol.PLAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushChannel() {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
public void pauseRecording() {
|
||||
resumeOnNextPacket = false;
|
||||
paused = true;
|
||||
}
|
||||
|
||||
public void resumeRecording() {
|
||||
resumeOnNextPacket = true;
|
||||
}
|
||||
|
||||
public void setWeather(RecorderOption.RecordWeather weather) {
|
||||
weather.getPackets().forEach(this::savePacket);
|
||||
}
|
||||
|
||||
public long getRecordedTime() {
|
||||
final long base = System.currentTimeMillis() - startTime;
|
||||
return base - timeShift;
|
||||
}
|
||||
|
||||
private synchronized long getCurrentTimeAndUpdate() {
|
||||
long now = getRecordedTime();
|
||||
if (paused) {
|
||||
if (resumeOnNextPacket) {
|
||||
paused = false;
|
||||
}
|
||||
timeShift += now - lastPacket;
|
||||
return lastPacket;
|
||||
}
|
||||
return lastPacket = now;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(@NotNull Packet<?> packet, @Nullable PacketSendListener callbacks, boolean flush) {
|
||||
if (!stopped) {
|
||||
if (packet instanceof BundlePacket<?> packet1) {
|
||||
packet1.subPackets().forEach(subPacket -> send(subPacket, null));
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet instanceof ClientboundAddEntityPacket packet1) {
|
||||
if (packet1.getType() == EntityType.PLAYER) {
|
||||
metaData.players.add(packet1.getUUID());
|
||||
saveMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof ClientboundDisconnectPacket) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (recorderOption.forceDayTime != -1 && packet instanceof ClientboundSetTimePacket packet1) {
|
||||
packet = new ClientboundSetTimePacket(packet1.dayTime(), recorderOption.forceDayTime, false);
|
||||
}
|
||||
|
||||
if (recorderOption.forceWeather != null && packet instanceof ClientboundGameEventPacket packet1) {
|
||||
ClientboundGameEventPacket.Type type = packet1.getEvent();
|
||||
if (type == ClientboundGameEventPacket.START_RAINING || type == ClientboundGameEventPacket.STOP_RAINING || type == ClientboundGameEventPacket.RAIN_LEVEL_CHANGE || type == ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (recorderOption.ignoreChat && (packet instanceof ClientboundSystemChatPacket || packet instanceof ClientboundPlayerChatPacket)) {
|
||||
return;
|
||||
}
|
||||
|
||||
savePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMetadata() {
|
||||
saveService.submit(() -> {
|
||||
try {
|
||||
replayFile.saveMetaData(metaData);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void savePacket(Packet<?> packet) {
|
||||
this.savePacket(packet, state);
|
||||
}
|
||||
|
||||
private void savePacket(Packet<?> packet, final ConnectionProtocol protocol) {
|
||||
try {
|
||||
final long timestamp = getCurrentTimeAndUpdate();
|
||||
saveService.submit(() -> {
|
||||
try {
|
||||
replayFile.savePacket(timestamp, packet, protocol);
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Error saving packet");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe("Error saving packet");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSaved() {
|
||||
return isSaved;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> saveRecording(File dest, boolean save) {
|
||||
isSaved = true;
|
||||
if (!isSaving) {
|
||||
isSaving = true;
|
||||
metaData.duration = (int) lastPacket;
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
saveMetadata();
|
||||
saveService.shutdown();
|
||||
boolean interrupted = false;
|
||||
try {
|
||||
saveService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
try {
|
||||
if (save) {
|
||||
replayFile.closeAndSave(dest);
|
||||
} else {
|
||||
replayFile.closeNotSave();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new CompletionException(e);
|
||||
} finally {
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}, runnable -> {
|
||||
final Thread thread = new Thread(runnable, "Recording file save thread");
|
||||
thread.start();
|
||||
});
|
||||
} else {
|
||||
LOGGER.warning("saveRecording() called twice");
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
throw new IllegalStateException("saveRecording() called twice");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user