Add support for more mod protocols (#73)
* Add Syncmatica protocols * Add AppleSkin protocols * Add BBOR Protocol * Add Jade Protocol * Add Xaero Map Protocol * Add Support REI protocol * Fixed an issue where the configuration file was not loading correctly. * move config file directory * add license & refactor code * update leaves fix https://github.com/LeavesMC/Leaves/issues/747#event-19986621829 * Update Luminol * fix up jade protocol * fix up rei --------- Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun> Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Bacteriawa <A3167717663@hotmail.com>
|
||||
Date: Sat, 30 Aug 2025 17:16:32 +0800
|
||||
Subject: [PATCH] Leaves: Leaves Fakeplayer
|
||||
Subject: [PATCH] Leaves: Fakeplayer
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
|
||||
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Mon, 3 Feb 2025 16:51:01 +0800
|
||||
Subject: [PATCH] Leaves: Syncmatica Protocol
|
||||
|
||||
This patch is Powered by Syncmatica(https://github.com/End-Tech/syncmatica)
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index e17f9629369f8653b484b4cc71de4874b5faa4d6..9be4bc9e78c22c12ba594b16dac6901023f0fd47 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -330,9 +330,12 @@ 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
|
||||
this.playerGameConnection = new io.papermc.paper.connection.PaperPlayerGameConnection(this); // Paper
|
||||
}
|
||||
|
||||
+ public final org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget exchangeTarget; // Leaves - Syncmatica Protocol
|
||||
+
|
||||
// Paper start - configuration phase API
|
||||
@Override
|
||||
public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
|
||||
@@ -0,0 +1,25 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Mon, 3 Feb 2025 13:03:42 +0800
|
||||
Subject: [PATCH] Leaves: BBOR Protocol
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 29788fedd54b8411c9c8872b4d83b037874fb5f0..24af6821694768058dc0146220877ab70066b5f9 100644
|
||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -754,6 +754,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||
|
||||
public void setLoaded(boolean loaded) {
|
||||
this.loaded = loaded;
|
||||
+ // Leaves start - bbor
|
||||
+ if (loaded) {
|
||||
+ org.leavesmc.leaves.protocol.BBORProtocol.onChunkLoaded(this);
|
||||
+ }
|
||||
+ // Leaves end - bbor
|
||||
}
|
||||
|
||||
public Level getLevel() {
|
||||
@@ -0,0 +1,114 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Mon, 3 Feb 2025 13:33:19 +0800
|
||||
Subject: [PATCH] Leaves: Jade Protocol
|
||||
|
||||
This patch is Powered by Jade(https://github.com/Snownee/Jade)
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
||||
index c1798db2972c8f2a343cf6e16fd9354ff212d906..a8d617b16ab2b2c0cdb289a0aa05fa171940cd7e 100644
|
||||
--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
||||
+++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
|
||||
@@ -63,7 +63,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 17f58246849ed407821a987b200cc765eb7943f9..ac27df3ba0ce9bbdf2f32ea87171fbb9407008d6 100644
|
||||
--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
|
||||
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
|
||||
@@ -254,7 +254,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 6901e629d941e22e64d83eed4e8cfee3165a96a1..fdc26c8d8c82c20534c57af2a0281b99998cc9f6 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/LootPool.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/LootPool.java
|
||||
@@ -37,7 +37,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 8612cdf7161f8ddff60a6478cc901318b8f958ba..07a962d647baa99b0e1bf3898a07cc914e91397e 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/LootTable.java
|
||||
@@ -50,7 +50,7 @@ public class LootTable {
|
||||
public static final LootTable EMPTY = new LootTable(LootContextParamSets.EMPTY, Optional.empty(), List.of(), List.of());
|
||||
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 eeaa49e9f70a18b5d39493aeff73f31b05ac2faa..8cd0403d7873c4c37caef75935b06b056c3d951d 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
|
||||
@@ -16,7 +16,7 @@ public abstract class CompositeEntryBase extends LootPoolEntryContainer {
|
||||
return "Empty children list";
|
||||
}
|
||||
};
|
||||
- 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 65e27bce9e59ef97bc8b914d646fba924d0f0877..a49bdcdf37b351436e0ba6d7865f10827c4e6ab4 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
|
||||
@@ -14,7 +14,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 141026601cd9a4561426b85fd1f8e7dc0544fbd7..a5d7ebb93c147bf0f806ac3c9b2dc4b878573944 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
|
||||
@@ -29,7 +29,7 @@ public class NestedLootTable extends LootPoolSingletonContainer {
|
||||
return "->{inline}";
|
||||
}
|
||||
};
|
||||
- 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 bae72197acc929c7ed3e964f156115d728eb2176..8f3094f42f3366a1313d70c0b27fbe5632b2082a 100644
|
||||
--- a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
|
||||
+++ b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
|
||||
@@ -12,7 +12,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) {
|
||||
@@ -0,0 +1,21 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Fri, 7 Feb 2025 14:23:43 +0800
|
||||
Subject: [PATCH] Leaves: Xaero Map Protocol
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 908ec29a6e285122d1a8a43dc547b74af2697442..1bf1731e43278b71423fee5d92401c48d6dabaca 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -1247,6 +1247,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));
|
||||
@@ -0,0 +1,43 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Date: Thu, 27 Mar 2025 13:04:35 +0800
|
||||
Subject: [PATCH] Leaves: Support REI protocol
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
|
||||
index 9bc0a9c3577d63a0ad5489bfd4c07d5006245c5f..bb2e891539b7eb49dc7925630695149aa531f672 100644
|
||||
--- a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
|
||||
+++ b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
|
||||
@@ -83,6 +83,12 @@ public class SmithingTransformRecipe implements SmithingRecipe {
|
||||
);
|
||||
}
|
||||
|
||||
+ // Leaves start - REI
|
||||
+ public SlotDisplay getResult() {
|
||||
+ return this.result.display();
|
||||
+ }
|
||||
+ // Leaves end - REI
|
||||
+
|
||||
// CraftBukkit start
|
||||
@Override
|
||||
public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) {
|
||||
diff --git a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
|
||||
index c324896afc2ee28ebb4d426ce4a469ee847ce24d..d5b26ff7be916e07e1162536cb2ddf5674c82f46 100644
|
||||
--- a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
|
||||
+++ b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
|
||||
@@ -85,6 +85,12 @@ public class SmithingTrimRecipe implements SmithingRecipe {
|
||||
return Optional.of(this.addition);
|
||||
}
|
||||
|
||||
+ // Leaves start
|
||||
+ public Holder<TrimPattern> pattern() {
|
||||
+ return pattern;
|
||||
+ }
|
||||
+ // Leaves end
|
||||
+
|
||||
@Override
|
||||
public RecipeSerializer<SmithingTrimRecipe> getSerializer() {
|
||||
return RecipeSerializer.SMITHING_TRIM;
|
||||
@@ -161,7 +161,7 @@ index 852e1ffef6a022caad7c8eff34091e50112a2290..8eb5d014d9ed688ffebaffb4ce0bb408
|
||||
if (entity instanceof EnderDragonPart complexPart) {
|
||||
if (complexPart.parentMob instanceof EnderDragon) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
index e040f9a7c9451fbe547dda56952410047a99ddc0..05753de195bd7f4ac2952183662a8cc9ec8f4ba0 100644
|
||||
index 317837a2a9b3b511b5cac4df4db248d4652816dc..a8253d1a3129c8258e9ead7c430e1bcf82f735d7 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
@@ -897,7 +897,11 @@ public class CraftEventFactory {
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "appleskin", directory = {"protocol"})
|
||||
public class AppleSkinProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable AppleSkin protocol support""")
|
||||
public static boolean enabled = false;
|
||||
@ConfigInfo(name = "sync-tick-interval", comments = """
|
||||
Set AppleSkin Synchronization Frequency (Unit: Game Ticks)""")
|
||||
public static int syncTickInterval = 20;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "bbor", directory = {"protocol"})
|
||||
public class BBORProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable BBOR protocol support""")
|
||||
public static boolean enabled = false;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "jade", directory = {"protocol"})
|
||||
public class JadeProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable Jade protocol support""")
|
||||
public static boolean enabled = false;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "rei", directory = {"protocol"})
|
||||
public class REIServerProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable Roughly Enough Items protocol support""")
|
||||
public static boolean enabled = false;
|
||||
}
|
||||
+12
-2
@@ -1,4 +1,4 @@
|
||||
package fun.bm.lophine.config.modules.function;
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.CommandSuggestions;
|
||||
@@ -9,54 +9,64 @@ import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "servux-protocol")
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "servux", directory = {"protocol"})
|
||||
public class ServuxProtocolConfig implements IConfigModule {
|
||||
@TransformedConfig(name = "entity-protocol", directory = {"function", "servux-protocol", "data"})
|
||||
@TransformedConfig(name = "entity-protocol", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "entity-protocol", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "entity-protocol", directory = {"data"})
|
||||
public static boolean entityProtocol = false;
|
||||
|
||||
@TransformedConfig(name = "hud-logger-protocol", directory = {"function", "servux-protocol"})
|
||||
@TransformedConfig(name = "hud-logger-protocol", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "hud-logger-protocol", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "hud-logger-protocol")
|
||||
public static boolean hudLoggerProtocol = false;
|
||||
|
||||
@TransformedConfig(name = "hud-metadata-protocol", directory = {"function", "servux-protocol"})
|
||||
@TransformedConfig(name = "hud-metadata-protocol", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "hud-metadata-protocol", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "hud-metadata-protocol")
|
||||
public static boolean hudMetadataProtocol = false;
|
||||
|
||||
@TransformedConfig(name = "hud-metadata-share-seed", directory = {"function", "servux-protocol"})
|
||||
@TransformedConfig(name = "hud-metadata-share-seed", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "hud-metadata-share-seed", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "hud-metadata-share-seed")
|
||||
public static boolean hudMetadataShareSeed = false;
|
||||
|
||||
@TransformedConfig(name = "structure-protocol", directory = {"function", "servux-protocol"})
|
||||
@TransformedConfig(name = "structure-protocol", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "structure-protocol", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "structure-protocol")
|
||||
public static boolean structureProtocol = false;
|
||||
|
||||
@TransformedConfig(name = "hud-enabled-loggers", directory = {"function", "servux-protocol"})
|
||||
@TransformedConfig(name = "hud-enabled-loggers", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "hud-enabled-loggers", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "hud-enabled-loggers")
|
||||
public static List<String> hudEnabledLoggers = List.of("tps", "mob_caps");
|
||||
|
||||
@TransformedConfig(name = "hud-update-interval", directory = {"function", "servux-protocol"})
|
||||
@TransformedConfig(name = "hud-update-interval", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "hud-update-interval", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "hud-update-interval")
|
||||
public static int hudUpdateInterval = 1;
|
||||
|
||||
@TransformedConfig(name = "litematics-enabled", directory = {"function", "servux-protocol", "litematics"})
|
||||
@TransformedConfig(name = "litematics-enabled", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "litematics-enabled", directory = {"misc", "survux-protocol"})
|
||||
@ConfigInfo(name = "litematics-enabled", directory = {"litematics"})
|
||||
public static boolean litematicsEnabled = false;
|
||||
|
||||
@TransformedConfig(name = "litematics-max-nbt-size", directory = {"function", "servux-protocol", "litematics"})
|
||||
@TransformedConfig(name = "litematics-max-nbt-size", directory = {"function", "survux-protocol"})
|
||||
@TransformedConfig(name = "litematics-max-nbt-size", directory = {"misc", "survux-protocol"})
|
||||
@CommandSuggestions(suggest = {"-1", "2097152"})
|
||||
@ConfigInfo(name = "litematics-max-nbt-size", directory = {"litematics"})
|
||||
public static int litematicsMaxNbtSize = 2097152;
|
||||
|
||||
@TransformedConfig(name = "litematics-print-max-delay-ticks", directory = {"function", "servux-protocol", "litematics"})
|
||||
@TransformedConfig(name = "litematics-print-max-delay-ticks", directory = {"function", "survux-protocol"})
|
||||
@CommandSuggestions(suggest = {"-1", "1200"})
|
||||
@ConfigInfo(name = "litematics-print-max-delay-ticks", directory = {"litematics"}, comments = "The max delay ticks for printing litematics, -1 to disable")
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "syncmatica", directory = {"protocol"})
|
||||
public class SyncmaticaProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable Syncmatica protocol support""")
|
||||
public static boolean enabled = false;
|
||||
@ConfigInfo(name = "useQuota", comments = """
|
||||
Is there a limit on the size of projection files?""")
|
||||
public static boolean useQuota = false;
|
||||
@ConfigInfo(name = "quota-Limit", comments = """
|
||||
Maximum Projection File Size (in bytes)""")
|
||||
public static int quotaLimit = 40000000;
|
||||
|
||||
public void onLoaded(CommentedFileConfig configInstance) {
|
||||
SyncmaticaProtocol.init(enabled);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package fun.bm.lophine.config.modules.function.protocol;
|
||||
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.enums.EnumConfigCategory;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "xaero-map", directory = {"protocol"})
|
||||
public class XaeroMapProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(name = "enabled", comments = """
|
||||
Enable Xaero World Map Protocol Support""")
|
||||
public static boolean enabled = false;
|
||||
@ConfigInfo(name = "xaeroMapServerID")
|
||||
public static int xaeroMapServerID = new Random().nextInt();
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import fun.bm.lophine.config.modules.function.protocol.AppleSkinProtocolConfig;
|
||||
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.Context;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@LeavesProtocol.Register(namespace = "appleskin")
|
||||
public class AppleSkinProtocol implements LeavesProtocol {
|
||||
|
||||
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<UUID, Set<String>> subscribedChannels = new HashMap<>();
|
||||
|
||||
@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.getUUID());
|
||||
resetPlayerData(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.MinecraftRegister(onlyNamespace = true)
|
||||
public static void onPlayerSubscribed(@NotNull Context context, ResourceLocation id) {
|
||||
subscribedChannels.computeIfAbsent(context.profile().getId(), k -> new HashSet<>()).add(id.getPath());
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker
|
||||
public static void tick() {
|
||||
for (Map.Entry<UUID, Set<String>> entry : subscribedChannels.entrySet()) {
|
||||
ServerPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(entry.getKey());
|
||||
if (player == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.sendBytebufPacket(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.sendBytebufPacket(player, EXHAUSTION_KEY, buf -> buf.writeFloat(exhaustion));
|
||||
previousExhaustionLevels.put(player, exhaustion);
|
||||
}
|
||||
}
|
||||
|
||||
case "natural_regeneration" -> {
|
||||
boolean regeneration = player.level().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION);
|
||||
Boolean previousRegeneration = previousNaturalRegeneration.get(player);
|
||||
if (previousRegeneration == null || regeneration != previousRegeneration) {
|
||||
ProtocolUtils.sendBytebufPacket(player, NATURAL_REGENERATION_KEY, buf -> buf.writeBoolean(regeneration));
|
||||
previousNaturalRegeneration.put(player, regeneration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadServer
|
||||
public static void onServerReload() {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int tickerInterval(String tickerID) {
|
||||
return AppleSkinProtocolConfig.syncTickInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return AppleSkinProtocolConfig.enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import fun.bm.lophine.config.modules.function.protocol.BBORProtocolConfig;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||
import net.minecraft.world.level.levelgen.structure.StructurePiece;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
||||
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.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@LeavesProtocol.Register(namespace = "bbor")
|
||||
public class BBORProtocol implements LeavesProtocol {
|
||||
|
||||
public static final String PROTOCOL_ID = "bbor";
|
||||
|
||||
// send
|
||||
private static final ResourceLocation INITIALIZE_CLIENT = id("initialize");
|
||||
private static final ResourceLocation ADD_BOUNDING_BOX = id("add_bounding_box_v2");
|
||||
private static final ResourceLocation STRUCTURE_LIST_SYNC = id("structure_list_sync_v1");
|
||||
// call
|
||||
private static final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
|
||||
private static final Map<Integer, Set<BBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
|
||||
private static final Map<ResourceLocation, Map<BBoundingBox, Set<BBoundingBox>>> dimensionCache = new ConcurrentHashMap<>();
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static ResourceLocation id(String path) {
|
||||
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker
|
||||
public static void tick() {
|
||||
for (var playerEntry : players.entrySet()) {
|
||||
sendBoundingToPlayer(playerEntry.getKey(), playerEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadServer
|
||||
public static void onServerReload() {
|
||||
if (BBORProtocolConfig.enabled) {
|
||||
initAllPlayer();
|
||||
} else {
|
||||
loggedOutAllPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerJoin
|
||||
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
|
||||
ServerLevel overworld = MinecraftServer.getServer().overworld();
|
||||
ProtocolUtils.sendBytebufPacket(player, INITIALIZE_CLIENT, buf -> {
|
||||
buf.writeLong(overworld.getSeed());
|
||||
buf.writeInt(overworld.levelData.getSpawnPos().getX());
|
||||
buf.writeInt(overworld.levelData.getSpawnPos().getZ());
|
||||
});
|
||||
sendStructureList(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
|
||||
players.remove(player.getId());
|
||||
playerBoundingBoxesCache.remove(player.getId());
|
||||
}
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "subscribe")
|
||||
public static void onPlayerSubscribed(@NotNull ServerPlayer player, FriendlyByteBuf buf) {
|
||||
players.put(player.getId(), player);
|
||||
sendBoundingToPlayer(player.getId(), player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadDataPack
|
||||
public static void onDataPackReload() {
|
||||
players.values().forEach(BBORProtocol::sendStructureList);
|
||||
}
|
||||
|
||||
public static void onChunkLoaded(@NotNull LevelChunk chunk) {
|
||||
Map<String, StructureStart> structures = new HashMap<>();
|
||||
final Registry<Structure> structureFeatureRegistry = chunk.getLevel().registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
for (var es : chunk.getAllStarts().entrySet()) {
|
||||
final var optional = structureFeatureRegistry.getResourceKey(es.getKey());
|
||||
optional.ifPresent(key -> structures.put(key.location().toString(), es.getValue()));
|
||||
}
|
||||
if (!structures.isEmpty()) {
|
||||
onStructuresLoaded(chunk.getLevel().dimension().location(), structures);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onStructuresLoaded(@NotNull ResourceLocation dimensionID, @NotNull Map<String, StructureStart> structures) {
|
||||
Map<BBoundingBox, Set<BBoundingBox>> cache = getOrCreateCache(dimensionID);
|
||||
for (var entry : structures.entrySet()) {
|
||||
StructureStart structureStart = entry.getValue();
|
||||
if (structureStart == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String type = "structure:" + entry.getKey();
|
||||
BoundingBox bb = structureStart.getBoundingBox();
|
||||
BBoundingBox boundingBox = buildStructure(bb, type);
|
||||
if (cache.containsKey(boundingBox)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<BBoundingBox> structureBoundingBoxes = new HashSet<>();
|
||||
for (StructurePiece structureComponent : structureStart.getPieces()) {
|
||||
structureBoundingBoxes.add(buildStructure(structureComponent.getBoundingBox(), type));
|
||||
}
|
||||
cache.put(boundingBox, structureBoundingBoxes);
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull BBoundingBox buildStructure(@NotNull BoundingBox bb, String type) {
|
||||
BlockPos min = new BlockPos(bb.minX(), bb.minY(), bb.minZ());
|
||||
BlockPos max = new BlockPos(bb.maxX(), bb.maxY(), bb.maxZ());
|
||||
return new BBoundingBox(type, min, max);
|
||||
}
|
||||
|
||||
private static void sendStructureList(@NotNull ServerPlayer player) {
|
||||
final Registry<Structure> structureRegistry = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
||||
final Set<String> structureIds = structureRegistry.entrySet().stream()
|
||||
.map(e -> e.getKey().location().toString()).collect(Collectors.toSet());
|
||||
ProtocolUtils.sendBytebufPacket(player, STRUCTURE_LIST_SYNC, buf -> {
|
||||
buf.writeVarInt(structureIds.size());
|
||||
structureIds.forEach(buf::writeUtf);
|
||||
});
|
||||
}
|
||||
|
||||
private static void sendBoundingToPlayer(int id, ServerPlayer player) {
|
||||
for (var entry : dimensionCache.entrySet()) {
|
||||
if (entry.getValue() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<BBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(id, k -> new HashSet<>());
|
||||
Map<BBoundingBox, Set<BBoundingBox>> boundingBoxMap = entry.getValue();
|
||||
for (BBoundingBox key : boundingBoxMap.keySet()) {
|
||||
if (playerBoundingBoxes.contains(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<BBoundingBox> boundingBoxes = boundingBoxMap.get(key);
|
||||
ProtocolUtils.sendBytebufPacket(player, ADD_BOUNDING_BOX, buf -> {
|
||||
buf.writeResourceLocation(entry.getKey());
|
||||
key.serialize(buf);
|
||||
if (boundingBoxes != null && boundingBoxes.size() > 1) {
|
||||
for (BBoundingBox box : boundingBoxes) {
|
||||
box.serialize(buf);
|
||||
}
|
||||
}
|
||||
});
|
||||
playerBoundingBoxes.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void initAllPlayer() {
|
||||
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
|
||||
onPlayerLoggedIn(player);
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public static void loggedOutAllPlayer() {
|
||||
players.clear();
|
||||
playerBoundingBoxesCache.clear();
|
||||
for (var cache : dimensionCache.values()) {
|
||||
cache.clear();
|
||||
}
|
||||
dimensionCache.clear();
|
||||
}
|
||||
|
||||
private static Map<BBoundingBox, Set<BBoundingBox>> getOrCreateCache(ResourceLocation dimensionId) {
|
||||
return dimensionCache.computeIfAbsent(dimensionId, dt -> new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
boolean active = BBORProtocolConfig.enabled;
|
||||
if (!active && initialized) {
|
||||
initialized = false;
|
||||
loggedOutAllPlayer();
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
private record BBoundingBox(String type, BlockPos min, BlockPos max) {
|
||||
|
||||
private static int combineHashCodes(int @NotNull ... hashCodes) {
|
||||
final int prime = 31;
|
||||
int result = 0;
|
||||
for (int hashCode : hashCodes) {
|
||||
result = prime * result + hashCode;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void serialize(@NotNull FriendlyByteBuf buf) {
|
||||
buf.writeChar('S');
|
||||
buf.writeInt(type.hashCode());
|
||||
buf.writeVarInt(min.getX()).writeVarInt(min.getY()).writeVarInt(min.getZ());
|
||||
buf.writeVarInt(max.getX()).writeVarInt(max.getY()).writeVarInt(max.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return combineHashCodes(min.hashCode(), max.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import fun.bm.lophine.config.modules.function.protocol.XaeroMapProtocolConfig;
|
||||
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.Register(namespace = "xaerominimap_or_xaeroworldmap_i_dont_care")
|
||||
public class XaeroMapProtocol implements LeavesProtocol {
|
||||
|
||||
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");
|
||||
|
||||
@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 (XaeroMapProtocolConfig.enabled) {
|
||||
ProtocolUtils.sendBytebufPacket(player, MINIMAP_KEY, buf -> {
|
||||
buf.writeByte(0);
|
||||
buf.writeInt(XaeroMapProtocolConfig.xaeroMapServerID);
|
||||
});
|
||||
ProtocolUtils.sendBytebufPacket(player, WORLDMAP_KEY, buf -> {
|
||||
buf.writeByte(0);
|
||||
buf.writeInt(XaeroMapProtocolConfig.xaeroMapServerID);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return XaeroMapProtocolConfig.enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.function.protocol.JadeProtocolConfig;
|
||||
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.animal.sniffer.Sniffer;
|
||||
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.*;
|
||||
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 org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
|
||||
import org.leavesmc.leaves.protocol.jade.payload.*;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.*;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.block.*;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.entity.*;
|
||||
import org.leavesmc.leaves.protocol.jade.util.*;
|
||||
import org.leavesmc.leaves.protocol.servux.litematics.utils.NbtUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@LeavesProtocol.Register(namespace = "jade")
|
||||
public class JadeProtocol implements LeavesProtocol {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static final String PROTOCOL_ID = "jade";
|
||||
public static final String PROTOCOL_VERSION = "8";
|
||||
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 PriorityStore<ResourceLocation, IJadeProvider> priorities;
|
||||
private static List<Block> shearableBlocks = null;
|
||||
|
||||
@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, BlockNameProvider.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(Sniffer.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);
|
||||
|
||||
itemStorageProviders.register(CampfireBlock.class, CampfireProvider.INSTANCE);
|
||||
|
||||
blockDataProviders.idMapped();
|
||||
entityDataProviders.idMapped();
|
||||
|
||||
blockDataProviders.loadComplete(priorities);
|
||||
entityDataProviders.loadComplete(priorities);
|
||||
itemStorageProviders.loadComplete(priorities);
|
||||
|
||||
rebuildShearableBlocks();
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = ClientHandshakePayload.class)
|
||||
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)
|
||||
public static void requestEntityData(ServerPlayer player, RequestEntityPayload payload) {
|
||||
player.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
|
||||
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) {
|
||||
LOGGER.warn("Error while saving data for entity " + entity);
|
||||
}
|
||||
}
|
||||
tag.putInt("EntityId", entity.getId());
|
||||
|
||||
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
|
||||
}, null, 1L);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = RequestBlockPayload.class)
|
||||
public static void requestBlockData(ServerPlayer player, RequestBlockPayload payload) {
|
||||
player.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
|
||||
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) {
|
||||
LOGGER.warn("Error while saving data for block " + accessor.getBlockState());
|
||||
}
|
||||
}
|
||||
NbtUtils.writeBlockPosToTag(pos, tag);
|
||||
tag.putString("BlockId", BuiltInRegistries.BLOCK.getKey(block).toString());
|
||||
|
||||
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
|
||||
}, null, 1L);
|
||||
}
|
||||
|
||||
@ProtocolHandler.ReloadServer
|
||||
public static void onServerReload() {
|
||||
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();
|
||||
LOGGER.warn("Failed to collect shearable blocks");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return JadeProtocolConfig.enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Accessor<T extends HitResult> {
|
||||
ServerLevel getLevel();
|
||||
|
||||
Player getPlayer();
|
||||
|
||||
<D> Tag encodeAsNbt(StreamEncoder<RegistryFriendlyByteBuf, D> codec, D value);
|
||||
|
||||
T getHitResult();
|
||||
|
||||
@Nullable
|
||||
Object getTarget();
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
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 ServerLevel level;
|
||||
private final Player player;
|
||||
private final Supplier<T> hit;
|
||||
protected boolean verify;
|
||||
private RegistryFriendlyByteBuf buffer;
|
||||
|
||||
public AccessorImpl(ServerLevel level, Player player, Supplier<T> hit) {
|
||||
this.level = level;
|
||||
this.player = player;
|
||||
this.hit = hit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevel 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();
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
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();
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
interface Builder {
|
||||
Builder level(ServerLevel level);
|
||||
|
||||
Builder player(Player player);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import net.minecraft.core.BlockPos;
|
||||
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.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
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;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 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));
|
||||
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();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return getBlockEntity();
|
||||
}
|
||||
|
||||
public static class Builder implements BlockAccessor.Builder {
|
||||
private ServerLevel level;
|
||||
private Player player;
|
||||
private BlockHitResult hit;
|
||||
private BlockState blockState = Blocks.AIR.defaultBlockState();
|
||||
private Supplier<BlockEntity> blockEntity;
|
||||
|
||||
@Override
|
||||
public Builder level(ServerLevel level) {
|
||||
this.level = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder player(Player player) {
|
||||
this.player = player;
|
||||
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();
|
||||
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)
|
||||
.hit(hit)
|
||||
.blockState(blockState)
|
||||
.blockEntity(blockEntity)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.accessor;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
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(ServerLevel level);
|
||||
|
||||
Builder player(Player player);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
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);
|
||||
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 {
|
||||
private ServerLevel level;
|
||||
private Player player;
|
||||
private Supplier<EntityHitResult> hit;
|
||||
private Supplier<Entity> entity;
|
||||
|
||||
@Override
|
||||
public Builder level(ServerLevel level) {
|
||||
this.level = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder player(Player player) {
|
||||
this.player = player;
|
||||
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();
|
||||
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)
|
||||
.entity(entity)
|
||||
.hit(Suppliers.memoize(() -> new EntityHitResult(entity.get(), hitVec)))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
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.jade.JadeProtocol;
|
||||
|
||||
public record ClientHandshakePayload(String protocolVersion) implements LeavesCustomPayload {
|
||||
|
||||
@ID
|
||||
private static final ResourceLocation PACKET_CLIENT_HANDSHAKE = JadeProtocol.id("client_handshake");
|
||||
|
||||
@Codec
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, ClientHandshakePayload> CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.STRING_UTF8, ClientHandshakePayload::protocolVersion, ClientHandshakePayload::new
|
||||
);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
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.jade.JadeProtocol;
|
||||
|
||||
public record ReceiveDataPayload(CompoundTag tag) implements LeavesCustomPayload {
|
||||
|
||||
@ID
|
||||
private static final ResourceLocation PACKET_RECEIVE_DATA = JadeProtocol.id("receive_data");
|
||||
|
||||
@Codec
|
||||
private static final StreamCodec<FriendlyByteBuf, ReceiveDataPayload> CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.COMPOUND_TAG, ReceiveDataPayload::tag, ReceiveDataPayload::new
|
||||
);
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
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 org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
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 {
|
||||
|
||||
@ID
|
||||
private static final ResourceLocation PACKET_REQUEST_BLOCK = JadeProtocol.id("request_block");
|
||||
|
||||
@Codec
|
||||
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);
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.payload;
|
||||
|
||||
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 org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
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 {
|
||||
|
||||
@ID
|
||||
private static final ResourceLocation PACKET_REQUEST_ENTITY = JadeProtocol.id("request_entity");
|
||||
|
||||
@Codec
|
||||
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);
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.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.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 {
|
||||
|
||||
@ID
|
||||
private static final ResourceLocation PACKET_SERVER_HANDSHAKE = JadeProtocol.id("server_handshake");
|
||||
|
||||
@Codec
|
||||
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
|
||||
);
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface IJadeProvider {
|
||||
|
||||
ResourceLocation getUid();
|
||||
|
||||
default int getDefaultPriority() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.logging.LogUtils;
|
||||
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.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 org.slf4j.Logger;
|
||||
|
||||
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");
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
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(),
|
||||
true // Bypass lock check
|
||||
);
|
||||
}
|
||||
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 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();
|
||||
}
|
||||
case EnderChestBlockEntity enderChest when request.getPlayer().getEnderChestInventory().isEmpty() -> {
|
||||
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) {
|
||||
LOGGER.warn("Failed to get item collector for " + target);
|
||||
return null;
|
||||
}
|
||||
|
||||
return itemCollector.update(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return UNIVERSAL_ITEM_STORAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPriority() {
|
||||
return 9999;
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 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 1000;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.provider;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
|
||||
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();
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 enum BlockNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
|
||||
INSTANCE;
|
||||
|
||||
private static final ResourceLocation CORE_OBJECT_NAME = JadeProtocol.id("object_name");
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.ItemStorageProvider;
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPriority() {
|
||||
return ItemStorageProvider.getBlock().getDefaultPriority() + 1;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
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;
|
||||
|
||||
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 @NotNull Data streamData(@NotNull BlockAccessor accessor) {
|
||||
AbstractFurnaceBlockEntity furnace = (AbstractFurnaceBlockEntity) accessor.getBlockEntity();
|
||||
return new Data(
|
||||
furnace.cookingTimer,
|
||||
furnace.cookingTotalTime,
|
||||
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);
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPriority() {
|
||||
return BlockNameProvider.INSTANCE.getDefaultPriority() + 10;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.TrialSpawnerStateData;
|
||||
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();
|
||||
TrialSpawnerStateData spawnerData = spawner.getTrialSpawner().getStateData();
|
||||
ServerLevel level = 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;
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.EntityReference;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
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");
|
||||
|
||||
public static UUID getOwnerUUID(Entity entity) {
|
||||
if (entity instanceof OwnableEntity ownableEntity) {
|
||||
EntityReference<LivingEntity> reference = ownableEntity.getOwnerReference();
|
||||
if (reference != null) {
|
||||
return reference.getUUID();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_ANIMAL_OWNER;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.provider.entity;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.animal.Chicken;
|
||||
import net.minecraft.world.entity.animal.armadillo.Armadillo;
|
||||
import net.minecraft.world.entity.animal.sniffer.Sniffer;
|
||||
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);
|
||||
}
|
||||
} else if (accessor.getEntity() instanceof Sniffer sniffer) {
|
||||
long time = sniffer.getBrain().getTimeUntilExpiry(MemoryModuleType.SNIFF_COOLDOWN);
|
||||
if (time > 0 && time < max) {
|
||||
tag.putInt("NextSniffIn", (int) time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getUid() {
|
||||
return MC_NEXT_ENTITY_DROP;
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.tool;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.Tool;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ShearsToolHandler {
|
||||
|
||||
private static final ShearsToolHandler INSTANCE = new ShearsToolHandler();
|
||||
|
||||
private final List<ItemStack> tools;
|
||||
|
||||
public ShearsToolHandler() {
|
||||
this.tools = List.of(Items.SHEARS.getDefaultInstance());
|
||||
}
|
||||
|
||||
public static ShearsToolHandler getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.logging.LogUtils;
|
||||
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.entity.SkullBlockEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IServerExtensionProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CommonUtil {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
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) {
|
||||
LOGGER.warn(e.toString());
|
||||
continue;
|
||||
}
|
||||
if (groups != null) {
|
||||
return Map.entry(provider.getUid(), groups);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.*;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class HierarchyLookup<T extends IJadeProvider> implements IHierarchyLookup<T> {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
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, list);
|
||||
if (singleton && !list.isEmpty()) {
|
||||
return ImmutableList.of(list.getFirst());
|
||||
}
|
||||
return list;
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
LOGGER.warn("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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.JadeProtocol;
|
||||
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface IHierarchyLookup<T extends IJadeProvider> {
|
||||
|
||||
Comparator<IJadeProvider> COMPARATOR = Comparator.comparingInt(provider -> JadeProtocol.priorities.byValue(provider));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 net.minecraft.world.item.component.TooltipDisplay;
|
||||
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> SHOWN = stack -> {
|
||||
if (stack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (stack.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT).hideTooltip()) {
|
||||
return false;
|
||||
}
|
||||
if (stack.hasNonDefault(DataComponents.CUSTOM_MODEL_DATA) || stack.hasNonDefault(DataComponents.ITEM_MODEL)) {
|
||||
CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
||||
for (String key : tag.keySet()) {
|
||||
if (key.toLowerCase(Locale.ENGLISH).endsWith("clear") && tag.getBooleanOr(key, true)) {
|
||||
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().getServer().getTickCount();
|
||||
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 (SHOWN.test(stack)) {
|
||||
ItemDefinition def = new ItemDefinition(stack);
|
||||
items.addTo(def, stack.getCount());
|
||||
}
|
||||
});
|
||||
iterator.afterPopulate(container, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 float progress;
|
||||
|
||||
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);
|
||||
|
||||
protected abstract int getSlotCount(T container);
|
||||
|
||||
public void reset() {
|
||||
currentIndex = fromIndex;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
public void afterPopulate(T container, int count) {
|
||||
currentIndex += count;
|
||||
if (count == 0 || currentIndex >= 10000) {
|
||||
finished = true;
|
||||
}
|
||||
progress = (float) (currentIndex - fromIndex) / (getSlotCount(container) - fromIndex);
|
||||
}
|
||||
|
||||
public float getCollectingProgress() {
|
||||
return Float.NaN;
|
||||
}
|
||||
|
||||
public static abstract class SlottedItemIterator<T> extends ItemIterator<T> {
|
||||
|
||||
public SlottedItemIterator(Function<Object, @Nullable T> containerFinder, int fromIndex) {
|
||||
super(containerFinder, fromIndex);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 com.mojang.logging.LogUtils;
|
||||
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.protocol.jade.provider.IJadeProvider;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
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> {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
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, Iterables.concat(firstList, secondList));
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
LOGGER.warn(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 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 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 CompoundTag getExtraData() {
|
||||
if (extraData == null) {
|
||||
extraData = new CompoundTag();
|
||||
}
|
||||
return extraData;
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.jade.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
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.Set;
|
||||
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) {
|
||||
Set<T> set = Sets.newLinkedHashSet();
|
||||
for (var override : overrides) {
|
||||
Object o = override.getRight().apply(accessor);
|
||||
if (o != null) {
|
||||
set.addAll(override.getLeft().get(o));
|
||||
}
|
||||
}
|
||||
set.addAll(get(accessor.getTarget()));
|
||||
return ImmutableList.sortedCopyOf(COMPARATOR, set);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class PacketTransformer {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private static final byte START = 0x0;
|
||||
private static final byte PART = 0x1;
|
||||
private static final byte END = 0x2;
|
||||
private static final byte ONLY = 0x3;
|
||||
|
||||
private final Map<UUID, PartData> cache = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public static DiscardedPayload wrapRei(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer());
|
||||
newBuf.writeByteArray(ByteBufUtil.getBytes(buf));
|
||||
return new DiscardedPayload(location, ByteBufUtil.getBytes(newBuf));
|
||||
}
|
||||
|
||||
public void inbound(ResourceLocation id, RegistryFriendlyByteBuf buf, ServerPlayer player, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||
UUID key = player.getUUID();
|
||||
PartData data;
|
||||
buf.readVarInt();
|
||||
switch (buf.readByte()) {
|
||||
case START -> {
|
||||
int partsNum = buf.readInt();
|
||||
data = new PartData(id, partsNum);
|
||||
if (cache.put(key, data) != null) {
|
||||
LOGGER.warn("Received invalid START packet for SplitPacketTransformer with packet id " + id);
|
||||
}
|
||||
buf.retain();
|
||||
data.parts.add(buf);
|
||||
}
|
||||
case PART -> {
|
||||
if ((data = cache.get(key)) == null) {
|
||||
LOGGER.warn("Received invalid PART packet for SplitPacketTransformer with packet id " + id);
|
||||
buf.release();
|
||||
} else if (!data.id.equals(id)) {
|
||||
LOGGER.warn("Received invalid PART packet for SplitPacketTransformer with packet id " + id + ", id in cache is {}" + data.id);
|
||||
buf.release();
|
||||
for (RegistryFriendlyByteBuf part : data.parts) {
|
||||
if (part != buf) {
|
||||
part.release();
|
||||
}
|
||||
}
|
||||
cache.remove(key);
|
||||
} else {
|
||||
buf.retain();
|
||||
data.parts.add(buf);
|
||||
}
|
||||
}
|
||||
case END -> {
|
||||
if ((data = cache.get(key)) == null) {
|
||||
LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id {}" + id);
|
||||
buf.release();
|
||||
} else if (!data.id.equals(id)) {
|
||||
LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + ", id in cache is {}" + data.id);
|
||||
buf.release();
|
||||
for (RegistryFriendlyByteBuf part : data.parts) {
|
||||
if (part != buf) {
|
||||
part.release();
|
||||
}
|
||||
}
|
||||
cache.remove(key);
|
||||
} else {
|
||||
buf.retain();
|
||||
data.parts.add(buf);
|
||||
}
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
if (data.parts.size() != data.partsNum) {
|
||||
LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + " with size " + data.parts + ", parts expected is {}" + data.partsNum);
|
||||
for (RegistryFriendlyByteBuf part : data.parts) {
|
||||
if (part != buf) {
|
||||
part.release();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RegistryFriendlyByteBuf byteBuf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(data.parts.toArray(new ByteBuf[0])), buf.registryAccess());
|
||||
consumer.accept(data.id, byteBuf);
|
||||
byteBuf.release();
|
||||
}
|
||||
cache.remove(key);
|
||||
}
|
||||
case ONLY -> consumer.accept(id, buf);
|
||||
default -> throw new IllegalStateException("Illegal split packet header!");
|
||||
}
|
||||
}
|
||||
|
||||
public void outbound(ResourceLocation id, RegistryFriendlyByteBuf buf, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||
int maxSize = 1048576 - 1 - 20 - id.toString().getBytes(StandardCharsets.UTF_8).length;
|
||||
if (buf.readableBytes() <= maxSize) {
|
||||
ByteBuf stateBuf = Unpooled.buffer(1);
|
||||
stateBuf.writeByte(ONLY);
|
||||
RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(stateBuf, buf), buf.registryAccess());
|
||||
consumer.accept(id, packetBuffer);
|
||||
} else {
|
||||
int partSize = maxSize - 4;
|
||||
int parts = (int) Math.ceil(buf.readableBytes() / (float) partSize);
|
||||
for (int i = 0; i < parts; i++) {
|
||||
RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), buf.registryAccess());
|
||||
if (i == 0) {
|
||||
packetBuffer.writeByte(START);
|
||||
packetBuffer.writeInt(parts);
|
||||
} else if (i == parts - 1) {
|
||||
packetBuffer.writeByte(END);
|
||||
} else {
|
||||
packetBuffer.writeByte(PART);
|
||||
}
|
||||
int next = Math.min(buf.readableBytes(), partSize);
|
||||
packetBuffer.writeBytes(buf.retainedSlice(buf.readerIndex(), next));
|
||||
buf.skipBytes(next);
|
||||
consumer.accept(id, packetBuffer);
|
||||
}
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PartData {
|
||||
private final ResourceLocation id;
|
||||
private final int partsNum;
|
||||
private final List<RegistryFriendlyByteBuf> parts;
|
||||
|
||||
public PartData(ResourceLocation id, int partsNum) {
|
||||
this.id = id;
|
||||
this.partsNum = partsNum;
|
||||
this.parts = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.function.protocol.REIServerProtocolConfig;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
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 net.minecraft.util.Mth;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.*;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.plugin.MinecraftInternalPlugin;
|
||||
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.rei.display.*;
|
||||
import org.leavesmc.leaves.protocol.rei.payload.DisplaySyncPayload;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.InputSlotCrafter;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.NewInputSlotCrafter;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.PlayerInventorySlotAccessor;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.SlotAccessor;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.VanillaSlotAccessor;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@LeavesProtocol.Register(namespace = REIServerProtocol.PROTOCOL_ID)
|
||||
public class REIServerProtocol implements LeavesProtocol {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static final String PROTOCOL_ID = "roughlyenoughitems";
|
||||
public static final String CHEAT_PERMISSION = "leaves.protocol.rei.cheat";
|
||||
public static final ResourceLocation DELETE_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "delete_item");
|
||||
public static final ResourceLocation CREATE_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item");
|
||||
public static final ResourceLocation CREATE_ITEMS_HOTBAR_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_hotbar");
|
||||
public static final ResourceLocation CREATE_ITEMS_GRAB_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_grab");
|
||||
public static final ResourceLocation CREATE_ITEMS_MESSAGE_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "ci_msg");
|
||||
public static final ResourceLocation MOVE_ITEMS_NEW_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "move_items_new");
|
||||
public static final ResourceLocation NOT_ENOUGH_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "og_not_enough"); // this pack is under to-do at rei-client, so we don't handle it
|
||||
public static final ResourceLocation SYNC_DISPLAYS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "sync_displays");
|
||||
|
||||
public static final Map<ResourceLocation, PacketTransformer> TRANSFORMERS = Util.make(() -> {
|
||||
ImmutableMap.Builder<ResourceLocation, PacketTransformer> builder = ImmutableMap.builder();
|
||||
builder.put(SYNC_DISPLAYS_PACKET, new PacketTransformer());
|
||||
builder.put(DELETE_ITEMS_PACKET, new PacketTransformer());
|
||||
builder.put(CREATE_ITEMS_PACKET, new PacketTransformer());
|
||||
builder.put(CREATE_ITEMS_GRAB_PACKET, new PacketTransformer());
|
||||
builder.put(CREATE_ITEMS_HOTBAR_PACKET, new PacketTransformer());
|
||||
builder.put(MOVE_ITEMS_NEW_PACKET, new PacketTransformer());
|
||||
return builder.build();
|
||||
});
|
||||
private static final Set<ServerPlayer> enabledPlayers = new HashSet<>();
|
||||
private static final Executor executor = new ThreadPoolExecutor(
|
||||
1, 1, 0L, TimeUnit.MILLISECONDS,
|
||||
new ArrayBlockingQueue<>(1),
|
||||
new ThreadPoolExecutor.DiscardOldestPolicy()
|
||||
);
|
||||
private static int minecraftRecipeVer = 0;
|
||||
private static int nextReiRecipeVer = -1;
|
||||
private static ImmutableList<CustomPacketPayload> cachedPayloads;
|
||||
|
||||
@ProtocolHandler.ReloadDataPack
|
||||
public static void onRecipeReload() {
|
||||
minecraftRecipeVer = MinecraftServer.getServer().getTickCount();
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static ResourceLocation id(String path) {
|
||||
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
|
||||
}
|
||||
|
||||
public static void onConfigModify(boolean enabled) {
|
||||
PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||
if (enabled) {
|
||||
if (pluginManager.getPermission(CHEAT_PERMISSION) == null) {
|
||||
pluginManager.addPermission(new Permission(CHEAT_PERMISSION, PermissionDefault.OP));
|
||||
}
|
||||
} else {
|
||||
pluginManager.removePermission(CHEAT_PERMISSION);
|
||||
enabledPlayers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
|
||||
enabledPlayers.remove(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker
|
||||
public static void tick() {
|
||||
if (minecraftRecipeVer != nextReiRecipeVer) {
|
||||
nextReiRecipeVer = minecraftRecipeVer;
|
||||
executor.execute(() -> reloadRecipe(nextReiRecipeVer));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static void reloadRecipe(int reiRecipeVer) {
|
||||
ImmutableList.Builder<Display> builder = ImmutableList.builder();
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
RecipeMap recipeMap = server.getRecipeManager().recipes;
|
||||
recipeMap.byType(RecipeType.CRAFTING).forEach(holder -> {
|
||||
switch (holder.value()) {
|
||||
case ShapedRecipe ignored -> builder.add(new ShapedDisplay((RecipeHolder) holder));
|
||||
case ShapelessRecipe ignored -> builder.add(new ShapelessDisplay((RecipeHolder) holder));
|
||||
case TransmuteRecipe ignored -> builder.addAll(Display.ofTransmuteRecipe((RecipeHolder) holder));
|
||||
case TippedArrowRecipe ignored -> builder.addAll(Display.ofTippedArrowRecipe((RecipeHolder) holder));
|
||||
case FireworkRocketRecipe ignored ->
|
||||
builder.addAll(Display.ofFireworkRocketRecipe((RecipeHolder) holder));
|
||||
case MapCloningRecipe ignored -> builder.addAll(Display.ofMapCloningRecipe((RecipeHolder) holder));
|
||||
// ignore ArmorDyeRecipe, BannerDuplicateRecipe, BookCloningRecipe, ShieldDecorationRecipe
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
});
|
||||
recipeMap.byType(RecipeType.STONECUTTING).forEach(holder -> builder.add(new StoneCuttingDisplay(holder)));
|
||||
recipeMap.byType(RecipeType.SMELTING).forEach(holder -> builder.add(new SmeltingDisplay(holder)));
|
||||
recipeMap.byType(RecipeType.BLASTING).forEach(holder -> builder.add(new BlastingDisplay(holder)));
|
||||
recipeMap.byType(RecipeType.SMOKING).forEach(holder -> builder.add(new SmokingDisplay(holder)));
|
||||
recipeMap.byType(RecipeType.CAMPFIRE_COOKING).forEach(holder -> builder.add(new CampfireDisplay(holder)));
|
||||
recipeMap.byType(RecipeType.SMITHING).forEach(holder -> {
|
||||
switch (holder.value()) {
|
||||
case SmithingTrimRecipe ignored -> builder.addAll(Display.ofSmithingTrimRecipe((RecipeHolder) holder));
|
||||
case SmithingTransformRecipe ignored -> builder.add(Display.ofTransforming((RecipeHolder) holder));
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
DisplaySyncPayload displaySyncPayload = new DisplaySyncPayload(
|
||||
DisplaySyncPayload.SyncType.SET,
|
||||
builder.build(),
|
||||
reiRecipeVer
|
||||
);
|
||||
|
||||
RegistryFriendlyByteBuf s2cBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||
DisplaySyncPayload.STREAM_CODEC.encode(s2cBuf, displaySyncPayload);
|
||||
ImmutableList.Builder<CustomPacketPayload> listBuilder = ImmutableList.builder();
|
||||
outboundTransform(s2cBuf, (id, splitBuf) ->
|
||||
listBuilder.add(PacketTransformer.wrapRei(id, splitBuf))
|
||||
);
|
||||
|
||||
cachedPayloads = listBuilder.build();
|
||||
Bukkit.getGlobalRegionScheduler().run(MinecraftInternalPlugin.INSTANCE, (task) -> {
|
||||
for (ServerPlayer player : enabledPlayers) {
|
||||
for (CustomPacketPayload payload : cachedPayloads) {
|
||||
ProtocolUtils.sendPayloadPacket(player, payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ProtocolHandler.MinecraftRegister(onlyNamespace = true, stage = ProtocolHandler.Stage.GAME)
|
||||
public static void onPlayerSubscribed(@NotNull ServerPlayer player, ResourceLocation location) {
|
||||
enabledPlayers.add(player);
|
||||
String channel = location.getPath();
|
||||
if (channel.equals("sync_displays")) {
|
||||
if (cachedPayloads != null) {
|
||||
cachedPayloads.forEach(payload -> ProtocolUtils.sendPayloadPacket(player, payload));
|
||||
}
|
||||
} else if (channel.equals("ci_msg")) {
|
||||
// cheat rei-client into using "delete_item" packet
|
||||
if (MinecraftServer.getServer().getProfilePermissions(player.getGameProfile()) < 1) {
|
||||
player.getBukkitEntity().sendOpLevel((byte) 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "delete_item")
|
||||
public static void handleDeleteItem(ServerPlayer player, RegistryFriendlyByteBuf buf) {
|
||||
if (!hasCheatPermission(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
inboundTransform(player, DELETE_ITEMS_PACKET, buf, (id, wholeBuf) -> {
|
||||
AbstractContainerMenu menu = player.containerMenu;
|
||||
if (!menu.getCarried().isEmpty()) {
|
||||
menu.setCarried(ItemStack.EMPTY);
|
||||
menu.broadcastChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "create_item")
|
||||
public static void handleCreateItem(ServerPlayer player, RegistryFriendlyByteBuf buf) {
|
||||
if (!hasCheatPermission(player)) {
|
||||
return;
|
||||
}
|
||||
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||
ItemStack itemStack = tmpBuf.readLenientJsonWithCodec(ItemStack.OPTIONAL_CODEC);
|
||||
if (player.getInventory().add(itemStack.copy())) {
|
||||
RegistryFriendlyByteBuf s2cWholeBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||
s2cWholeBuf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, itemStack.copy());
|
||||
s2cWholeBuf.writeUtf(player.getScoreboardName(), 32767);
|
||||
// Due to the bug in REI, no packets are actually sent here.
|
||||
/*
|
||||
outboundTransform(CREATE_ITEMS_MESSAGE_PACKET, s2cWholeBuf, (id, s2cSplitBuf) -> {
|
||||
ProtocolUtils.sendPayloadPacket(player, new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(s2cSplitBuf)));
|
||||
});
|
||||
*/
|
||||
} else {
|
||||
player.displayClientMessage(Component.translatable("text.rei.failed_cheat_items"), false);
|
||||
}
|
||||
};
|
||||
inboundTransform(player, CREATE_ITEMS_PACKET, buf, consumer);
|
||||
}
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "create_item_grab")
|
||||
public static void handleCreateItemGrab(ServerPlayer player, RegistryFriendlyByteBuf buf) {
|
||||
if (!hasCheatPermission(player)) {
|
||||
return;
|
||||
}
|
||||
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||
ItemStack itemStack = tmpBuf.readLenientJsonWithCodec(ItemStack.OPTIONAL_CODEC);
|
||||
ItemStack stack = itemStack.copy();
|
||||
AbstractContainerMenu menu = player.containerMenu;
|
||||
if (!menu.getCarried().isEmpty() && ItemStack.isSameItemSameComponents(menu.getCarried(), stack)) {
|
||||
stack.setCount(Mth.clamp(stack.getCount() + menu.getCarried().getCount(), 1, stack.getMaxStackSize()));
|
||||
} else if (!menu.getCarried().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
menu.setCarried(stack.copy());
|
||||
menu.broadcastChanges();
|
||||
RegistryFriendlyByteBuf s2cWholeBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||
s2cWholeBuf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, stack.copy());
|
||||
s2cWholeBuf.writeUtf(player.getScoreboardName(), 32767);
|
||||
// Due to the bug in REI, no packets are actually sent here.
|
||||
/*
|
||||
outboundTransform(CREATE_ITEMS_MESSAGE_PACKET, s2cWholeBuf, (id, s2cSplitBuf) -> {
|
||||
ProtocolUtils.sendPayloadPacket(player, new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(s2cSplitBuf)));
|
||||
});
|
||||
*/
|
||||
};
|
||||
inboundTransform(player, CREATE_ITEMS_GRAB_PACKET, buf, consumer);
|
||||
}
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "create_item_hotbar")
|
||||
public static void handleCreateItemHotbar(ServerPlayer player, RegistryFriendlyByteBuf buf) {
|
||||
if (!hasCheatPermission(player)) {
|
||||
return;
|
||||
}
|
||||
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||
ItemStack stack = tmpBuf.readLenientJsonWithCodec(ItemStack.OPTIONAL_CODEC);
|
||||
int hotbarSlotId = tmpBuf.readVarInt();
|
||||
if (hotbarSlotId >= 0 && hotbarSlotId < 9) {
|
||||
AbstractContainerMenu menu = player.containerMenu;
|
||||
player.getInventory().getNonEquipmentItems().set(hotbarSlotId, stack.copy());
|
||||
menu.broadcastChanges();
|
||||
RegistryFriendlyByteBuf s2cWholeBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||
s2cWholeBuf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, stack.copy());
|
||||
s2cWholeBuf.writeUtf(player.getScoreboardName(), 32767);
|
||||
// Due to the bug in REI, no packets are actually sent here.
|
||||
/*
|
||||
outboundTransform(CREATE_ITEMS_MESSAGE_PACKET, s2cWholeBuf, (id, s2cSplitBuf) -> {
|
||||
ProtocolUtils.sendPayloadPacket(player, new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(s2cSplitBuf)));
|
||||
});
|
||||
*/
|
||||
} else {
|
||||
player.displayClientMessage(Component.translatable("text.rei.failed_cheat_items"), false);
|
||||
}
|
||||
};
|
||||
inboundTransform(player, CREATE_ITEMS_HOTBAR_PACKET, buf, consumer);
|
||||
}
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "move_items_new")
|
||||
public static void handleMoveItem(ServerPlayer player, RegistryFriendlyByteBuf buf) {
|
||||
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||
AbstractContainerMenu container = player.containerMenu;
|
||||
tmpBuf.readResourceLocation();
|
||||
try {
|
||||
boolean shift = tmpBuf.readBoolean();
|
||||
try {
|
||||
CompoundTag nbt = tmpBuf.readNbt();
|
||||
if (nbt == null) {
|
||||
throw new IllegalStateException("NBT data is null");
|
||||
}
|
||||
int version = nbt.getInt("Version").orElse(-1);
|
||||
if (version != 1) {
|
||||
throw new IllegalStateException("Server and client REI protocol version mismatch! Server: 1, Client: " + version);
|
||||
}
|
||||
|
||||
List<List<ItemStack>> recipes = readInputs(player.registryAccess(), nbt.getListOrEmpty("Inputs"));
|
||||
List<SlotAccessor> input = readSlots(container, player, nbt.getListOrEmpty("InputSlots"));
|
||||
List<SlotAccessor> inventory = readSlots(container, player, nbt.getListOrEmpty("InventorySlots"));
|
||||
NewInputSlotCrafter<AbstractContainerMenu> crafter = new NewInputSlotCrafter<>(container, input, inventory, recipes);
|
||||
Bukkit.getGlobalRegionScheduler().run(MinecraftInternalPlugin.INSTANCE, (task) -> {
|
||||
try {
|
||||
crafter.fillInputSlots(player, shift);
|
||||
} catch (InputSlotCrafter.NotEnoughMaterialsException ignored1) {
|
||||
} catch (IllegalStateException e) {
|
||||
player.sendSystemMessage(Component.translatable(e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
player.sendSystemMessage(Component.translatable("error.rei.internal.error", e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
LOGGER.warn("Failed to move items for player " + player.getScoreboardName(), e);
|
||||
}
|
||||
});
|
||||
} catch (IllegalStateException e) {
|
||||
player.sendSystemMessage(Component.translatable(e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
player.sendSystemMessage(Component.translatable("error.rei.internal.error", e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
LOGGER.warn("Failed to move items for player " + player.getScoreboardName(), e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to move items for player " + player.getScoreboardName(), e);
|
||||
}
|
||||
};
|
||||
inboundTransform(player, MOVE_ITEMS_NEW_PACKET, buf, consumer);
|
||||
}
|
||||
|
||||
private static void inboundTransform(ServerPlayer player, ResourceLocation id, RegistryFriendlyByteBuf buf, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||
PacketTransformer transformer = TRANSFORMERS.get(id);
|
||||
if (transformer != null) {
|
||||
transformer.inbound(id, buf, player, consumer);
|
||||
} else {
|
||||
consumer.accept(id, buf);
|
||||
}
|
||||
}
|
||||
|
||||
private static void outboundTransform(RegistryFriendlyByteBuf buf, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||
PacketTransformer transformer = TRANSFORMERS.get(SYNC_DISPLAYS_PACKET);
|
||||
if (transformer != null) {
|
||||
transformer.outbound(SYNC_DISPLAYS_PACKET, buf, consumer);
|
||||
} else {
|
||||
consumer.accept(SYNC_DISPLAYS_PACKET, buf);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasCheatPermission(ServerPlayer player) {
|
||||
if (player.getBukkitEntity().hasPermission(CHEAT_PERMISSION)) {
|
||||
return true;
|
||||
}
|
||||
player.displayClientMessage(Component.translatable("text.rei.no_permission_cheat").withStyle(ChatFormatting.RED), false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return REIServerProtocolConfig.enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int tickerInterval(String tickerID) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
private static List<List<ItemStack>> readInputs(RegistryAccess registryAccess, ListTag tag) {
|
||||
List<List<ItemStack>> items = new ArrayList<>();
|
||||
for (Tag t : tag) {
|
||||
CompoundTag compoundTag = (CompoundTag) t;
|
||||
compoundTag.getInt("Index").orElseThrow();
|
||||
ListTag ingredientList = compoundTag.getListOrEmpty("Ingredient");
|
||||
List<ItemStack> slotItems = new ArrayList<>();
|
||||
for (Tag ingredient : ingredientList) {
|
||||
CompoundTag ingredientTag = (CompoundTag) ingredient;
|
||||
ItemStack stack = ItemStack.OPTIONAL_CODEC.parse(
|
||||
registryAccess.createSerializationContext(NbtOps.INSTANCE),
|
||||
ingredientTag.get("value")
|
||||
).getOrThrow();
|
||||
slotItems.add(stack);
|
||||
}
|
||||
items.add(slotItems);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private static List<SlotAccessor> readSlots(AbstractContainerMenu menu, ServerPlayer player, ListTag tag) {
|
||||
List<SlotAccessor> slots = new ArrayList<>();
|
||||
for (Tag t : tag) {
|
||||
CompoundTag compoundTag = (CompoundTag) t;
|
||||
String id = compoundTag.getString("id").orElseThrow();
|
||||
if (!id.startsWith(PROTOCOL_ID + ":")) {
|
||||
throw new IllegalStateException("Invalid slot id: " + id + ", expected to start with '" + PROTOCOL_ID + ":'");
|
||||
}
|
||||
id = id.substring((PROTOCOL_ID + ":").length());
|
||||
int slot = compoundTag.getInt("Slot").orElseThrow();
|
||||
SlotAccessor accessor = switch (id) {
|
||||
case "vanilla" -> new VanillaSlotAccessor(menu.slots.get(slot));
|
||||
case "player" -> new PlayerInventorySlotAccessor(player, slot);
|
||||
default -> throw new IllegalStateException("Unknown container id: " + id);
|
||||
};
|
||||
slots.add(accessor);
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.AbstractCookingRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
|
||||
public class BlastingDisplay extends CookingDisplay {
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/blasting");
|
||||
|
||||
public BlastingDisplay(RecipeHolder<? extends AbstractCookingRecipe> recipe) {
|
||||
super(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.CampfireCookingRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
|
||||
public class CampfireDisplay extends CookingDisplay {
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/campfire");
|
||||
|
||||
public CampfireDisplay(RecipeHolder<CampfireCookingRecipe> recipe) {
|
||||
super(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
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.item.crafting.AbstractCookingRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.SingleRecipeInput;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class CookingDisplay extends Display {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, CookingDisplay> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CookingDisplay::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CookingDisplay::getOutputEntries,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
CookingDisplay::getOptionalLocation,
|
||||
ByteBufCodecs.FLOAT,
|
||||
CookingDisplay::getXp,
|
||||
ByteBufCodecs.DOUBLE,
|
||||
CookingDisplay::getCookTime,
|
||||
CookingDisplay::of
|
||||
);
|
||||
protected float xp;
|
||||
protected double cookTime;
|
||||
|
||||
private CookingDisplay(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull ResourceLocation id, float xp, double cookTime) {
|
||||
super(inputs, outputs, id);
|
||||
this.xp = xp;
|
||||
this.cookTime = cookTime;
|
||||
}
|
||||
|
||||
public CookingDisplay(RecipeHolder<? extends AbstractCookingRecipe> recipe) {
|
||||
this(
|
||||
List.of(EntryIngredient.ofIngredient(recipe.value().input())),
|
||||
List.of(EntryIngredient.of(recipe.value().assemble(new SingleRecipeInput(ItemStack.EMPTY), CraftRegistry.getMinecraftRegistry()))),
|
||||
recipe.id().location(),
|
||||
recipe.value().experience(),
|
||||
recipe.value().cookingTime()
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static CookingDisplay of(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull Optional<ResourceLocation> id, float xp, double cookTime) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public float getXp() {
|
||||
return xp;
|
||||
}
|
||||
|
||||
public double getCookTime() {
|
||||
return cookTime;
|
||||
}
|
||||
|
||||
public StreamCodec<RegistryFriendlyByteBuf, CookingDisplay> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class CraftingDisplay extends Display {
|
||||
|
||||
public CraftingDisplay(@NotNull List<EntryIngredient> inputs,
|
||||
@NotNull List<EntryIngredient> outputs,
|
||||
@NotNull ResourceLocation location) {
|
||||
super(inputs, outputs, location);
|
||||
}
|
||||
|
||||
public abstract int getWidth();
|
||||
|
||||
public abstract int getHeight();
|
||||
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
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.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CustomDisplay extends CraftingDisplay {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, CustomDisplay> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CustomDisplay::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CustomDisplay::getOutputEntries,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
CustomDisplay::getOptionalLocation,
|
||||
CustomDisplay::of
|
||||
);
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/crafting/custom");
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomDisplay#DefaultCustomDisplay
|
||||
*/
|
||||
public CustomDisplay(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull ResourceLocation location) {
|
||||
super(inputs, outputs, location);
|
||||
BitSet row = new BitSet(3);
|
||||
BitSet column = new BitSet(3);
|
||||
for (int i = 0; i < 9; i++) {
|
||||
if (i < inputs.size()) {
|
||||
EntryIngredient stacks = inputs.get(i);
|
||||
if (stacks.stream().anyMatch(stack -> !stack.isEmpty())) {
|
||||
row.set((i - (i % 3)) / 3);
|
||||
column.set(i % 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.width = column.cardinality();
|
||||
this.height = row.cardinality();
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static CustomDisplay of(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull Optional<ResourceLocation> id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
|
||||
public StreamCodec<RegistryFriendlyByteBuf, CustomDisplay> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.util.context.ContextMap;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.alchemy.PotionContents;
|
||||
import net.minecraft.world.item.component.Fireworks;
|
||||
import net.minecraft.world.item.component.ProvidesTrimMaterial;
|
||||
import net.minecraft.world.item.crafting.*;
|
||||
import net.minecraft.world.item.crafting.display.*;
|
||||
import net.minecraft.world.item.equipment.trim.TrimMaterial;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A display to be used alongside Roughly Enough Items.
|
||||
* <p>
|
||||
* see me.shedaniel.rei.api.common.display.Display
|
||||
*/
|
||||
public abstract class Display {
|
||||
|
||||
protected ResourceLocation id;
|
||||
|
||||
protected List<EntryIngredient> inputs;
|
||||
|
||||
protected List<EntryIngredient> outputs;
|
||||
|
||||
public Display(@NotNull List<EntryIngredient> inputs,
|
||||
@NotNull List<EntryIngredient> outputs,
|
||||
@NotNull ResourceLocation id) {
|
||||
this.inputs = inputs;
|
||||
this.outputs = outputs;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static StreamCodec<RegistryFriendlyByteBuf, Display> dispatchCodec() {
|
||||
return new StreamCodec<>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Display decode(@NotNull RegistryFriendlyByteBuf buffer) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(@NotNull RegistryFriendlyByteBuf buffer, @NotNull Display display) {
|
||||
new FriendlyByteBuf(buffer).writeResourceLocation(display.getSerializerId());
|
||||
((StreamCodec<RegistryFriendlyByteBuf, Display>) display.streamCodec()).encode(buffer, display);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Collection<Display> ofTransmuteRecipe(@NotNull RecipeHolder<TransmuteRecipe> recipeHolder) {
|
||||
TransmuteRecipe recipe = recipeHolder.value();
|
||||
List<RecipeDisplay> displays = recipe.display();
|
||||
List<Display> displayList = new ArrayList<>();
|
||||
if (!displays.isEmpty()) {
|
||||
RecipeDisplay recipeDisplay = displays.getFirst();
|
||||
if (recipeDisplay instanceof ShapelessCraftingRecipeDisplay shapelessRecipeDisplay) {
|
||||
displayList.add(new ShapelessDisplay(shapelessRecipeDisplay, recipeHolder.id().location()));
|
||||
} else if (recipeDisplay instanceof ShapedCraftingRecipeDisplay shapelessRecipe) {
|
||||
displayList.add(new ShapedDisplay(shapelessRecipe, recipeHolder.id().location()));
|
||||
}
|
||||
}
|
||||
return displayList;
|
||||
}
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.client.categories.crafting.filler.TippedArrowRecipeFiller#apply
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<Display> ofTippedArrowRecipe(@NotNull RecipeHolder<TippedArrowRecipe> recipeHolder) {
|
||||
EntryIngredient arrowIngredient = EntryIngredient.of(Items.ARROW);
|
||||
Set<ResourceLocation> registeredPotions = new HashSet<>();
|
||||
List<Display> displays = new ArrayList<>();
|
||||
MinecraftServer.getServer().registryAccess().lookup(Registries.POTION).stream()
|
||||
.flatMap(Registry::listElements)
|
||||
.map(reference -> PotionContents.createItemStack(Items.LINGERING_POTION, reference))
|
||||
.forEach(itemStack -> {
|
||||
PotionContents potion = itemStack.get(DataComponents.POTION_CONTENTS);
|
||||
if (potion == null || potion.potion().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (potion.potion().get().unwrapKey().isPresent() && registeredPotions.add(potion.potion().get().unwrapKey().get().location())) {
|
||||
List<EntryIngredient> input = new ArrayList<>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
input.add(arrowIngredient);
|
||||
}
|
||||
input.add(EntryIngredient.of(itemStack));
|
||||
for (int i = 0; i < 4; i++) {
|
||||
input.add(arrowIngredient);
|
||||
}
|
||||
ItemStack outputStack = new ItemStack(Items.TIPPED_ARROW, 8);
|
||||
outputStack.set(DataComponents.POTION_CONTENTS, potion);
|
||||
displays.add(new CustomDisplay(input, List.of(EntryIngredient.of(outputStack)), recipeHolder.id().location()));
|
||||
}
|
||||
});
|
||||
return displays;
|
||||
}
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.client.categories.crafting.filler.TippedArrowRecipeFiller#apply
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<Display> ofFireworkRocketRecipe(@NotNull RecipeHolder<FireworkRocketRecipe> recipeHolder) {
|
||||
EntryIngredient[] inputs = new EntryIngredient[4];
|
||||
inputs[0] = EntryIngredient.of(Items.GUNPOWDER);
|
||||
inputs[1] = EntryIngredient.of(Items.PAPER);
|
||||
inputs[2] = EntryIngredient.of(new ItemStack(Items.AIR), new ItemStack(Items.GUNPOWDER), new ItemStack(Items.GUNPOWDER));
|
||||
inputs[3] = EntryIngredient.of(new ItemStack(Items.AIR), new ItemStack(Items.AIR), new ItemStack(Items.GUNPOWDER));
|
||||
ItemStack[] outputs = new ItemStack[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
outputs[i] = new ItemStack(Items.FIREWORK_ROCKET, 3);
|
||||
outputs[i].set(DataComponents.FIREWORKS, new Fireworks(i + 1, List.of()));
|
||||
}
|
||||
return Collections.singleton(new ShapelessDisplay(List.of(inputs), List.of(EntryIngredient.of(outputs)), recipeHolder.id().location()));
|
||||
}
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.client.categories.crafting.filler.MapCloningRecipeFiller#apply
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<Display> ofMapCloningRecipe(@NotNull RecipeHolder<MapCloningRecipe> recipeHolder) {
|
||||
return Collections.singleton(
|
||||
new ShapelessDisplay(
|
||||
List.of(EntryIngredient.of(Items.FILLED_MAP), EntryIngredient.of(Items.MAP)),
|
||||
List.of(EntryIngredient.of(new ItemStack(Items.FILLED_MAP, 2))),
|
||||
recipeHolder.id().location())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.common.displays.DefaultSmithingDisplay#ofTransforming
|
||||
*/
|
||||
@NotNull
|
||||
public static SmithingDisplay ofTransforming(RecipeHolder<SmithingTransformRecipe> recipeHolder) {
|
||||
return new SmithingDisplay(
|
||||
List.of(
|
||||
recipeHolder.value().templateIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty()),
|
||||
EntryIngredient.ofIngredient(recipeHolder.value().baseIngredient()),
|
||||
recipeHolder.value().additionIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty())
|
||||
),
|
||||
List.of(ofSlotDisplay(recipeHolder.value().getResult())),
|
||||
SmithingDisplay.SmithingRecipeType.TRANSFORM,
|
||||
recipeHolder.id().location()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.common.displays.DefaultSmithingDisplay#fromTrimming
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@NotNull
|
||||
public static Collection<Display> ofSmithingTrimRecipe(@NotNull RecipeHolder<SmithingTrimRecipe> recipeHolder) {
|
||||
RegistryAccess registryAccess = MinecraftServer.getServer().registryAccess();
|
||||
SmithingTrimRecipe recipe = recipeHolder.value();
|
||||
List<Display> displays = new ArrayList<>();
|
||||
for (Holder<Item> additionStack : (Iterable<Holder<Item>>) recipe.additionIngredient().map(Ingredient::items).orElse(Stream.of())::iterator) {
|
||||
Holder<TrimMaterial> trimMaterial = getMaterialFromIngredient(registryAccess, additionStack).orElse(null);
|
||||
if (trimMaterial == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EntryIngredient baseIngredient = EntryIngredient.ofIngredient(recipe.baseIngredient());
|
||||
displays.add(new SmithingDisplay.Trimming(List.of(
|
||||
recipe.templateIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty()),
|
||||
baseIngredient,
|
||||
EntryIngredient.ofItemHolder(additionStack)
|
||||
), List.of(baseIngredient), SmithingDisplay.SmithingRecipeType.TRIM, recipeHolder.id().location(), recipe.pattern()));
|
||||
|
||||
}
|
||||
return displays;
|
||||
}
|
||||
|
||||
private static Optional<Holder<TrimMaterial>> getMaterialFromIngredient(HolderLookup.Provider provider, Holder<Item> item) {
|
||||
ProvidesTrimMaterial providesTrimMaterial = new ItemStack(item).get(DataComponents.PROVIDES_TRIM_MATERIAL);
|
||||
return providesTrimMaterial != null ? providesTrimMaterial.unwrap(provider) : Optional.empty();
|
||||
}
|
||||
|
||||
public static EntryIngredient ofSlotDisplay(SlotDisplay slot) {
|
||||
return switch (slot) {
|
||||
case SlotDisplay.Empty ignored -> EntryIngredient.empty();
|
||||
case SlotDisplay.ItemSlotDisplay s -> EntryIngredient.of(s.item().value());
|
||||
case SlotDisplay.ItemStackSlotDisplay s -> EntryIngredient.of(s.stack());
|
||||
case SlotDisplay.TagSlotDisplay s -> ofItemTag(s.tag());
|
||||
case SlotDisplay.Composite s -> {
|
||||
ArrayList<ItemStack> list = new ArrayList<>();
|
||||
for (SlotDisplay slotDisplay : s.contents()) {
|
||||
ofSlotDisplay(slotDisplay).stream().forEach(list::add);
|
||||
}
|
||||
yield EntryIngredient.of(list.toArray(new ItemStack[0]));
|
||||
}
|
||||
// REI Bad idea
|
||||
case SlotDisplay.AnyFuel ignored -> EntryIngredient.empty();
|
||||
default -> {
|
||||
RegistryAccess access = MinecraftServer.getServer().registryAccess();
|
||||
try {
|
||||
List<ItemStack> stacks = slot.resolveForStacks(new ContextMap.Builder()
|
||||
.withParameter(SlotDisplayContext.REGISTRIES, access)
|
||||
.create(SlotDisplayContext.CONTEXT));
|
||||
yield EntryIngredient.of(stacks.toArray(new ItemStack[0]));
|
||||
} catch (Exception e) {
|
||||
MinecraftServer.LOGGER.warn("Failed to resolve slot display: {}", slot, e);
|
||||
yield EntryIngredient.empty();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static List<EntryIngredient> ofSlotDisplays(Collection<SlotDisplay> slots) {
|
||||
if (slots instanceof Collection<?> collection && collection.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ImmutableList.Builder<EntryIngredient> ingredients = ImmutableList.builder();
|
||||
for (SlotDisplay slot : slots) {
|
||||
ingredients.add(ofSlotDisplay(slot));
|
||||
}
|
||||
return ingredients.build();
|
||||
}
|
||||
|
||||
public static <T extends ItemLike> EntryIngredient ofItemTag(TagKey<T> tagKey) {
|
||||
HolderGetter<T> getter = MinecraftServer.getServer().registryAccess().lookupOrThrow(tagKey.registry());
|
||||
HolderSet.Named<T> holders = getter.get(tagKey).orElse(null);
|
||||
if (holders == null) {
|
||||
return EntryIngredient.empty();
|
||||
}
|
||||
|
||||
int size = holders.size();
|
||||
if (size == 0) {
|
||||
return EntryIngredient.empty();
|
||||
}
|
||||
if (size == 1) {
|
||||
return EntryIngredient.of(new ItemStack(holders.get(0).value()));
|
||||
}
|
||||
|
||||
List<ItemStack> stackList = new ArrayList<>();
|
||||
for (Holder<T> t : holders) {
|
||||
ItemStack stack = new ItemStack(t.value());
|
||||
if (!stack.isEmpty()) {
|
||||
stackList.add(stack);
|
||||
}
|
||||
}
|
||||
return EntryIngredient.of(stackList.toArray(new ItemStack[0]));
|
||||
}
|
||||
|
||||
public List<EntryIngredient> getInputEntries() {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
public List<EntryIngredient> getOutputEntries() {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public ResourceLocation getDisplayLocation() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Optional<ResourceLocation> getOptionalLocation() {
|
||||
return Optional.ofNullable(id);
|
||||
}
|
||||
|
||||
public abstract ResourceLocation getSerializerId();
|
||||
|
||||
public abstract StreamCodec<RegistryFriendlyByteBuf, ? extends Display> streamCodec();
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
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.item.crafting.CraftingInput;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.ShapedRecipe;
|
||||
import net.minecraft.world.item.crafting.display.ShapedCraftingRecipeDisplay;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.common.displays.crafting.DefaultShapedDisplay#DefaultShapedDisplay(RecipeHolder)
|
||||
*/
|
||||
public class ShapedDisplay extends CraftingDisplay {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CraftingDisplay::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CraftingDisplay::getOutputEntries,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
CraftingDisplay::getOptionalLocation,
|
||||
ByteBufCodecs.INT,
|
||||
CraftingDisplay::getWidth,
|
||||
ByteBufCodecs.INT,
|
||||
CraftingDisplay::getHeight,
|
||||
ShapedDisplay::of
|
||||
);
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/crafting/shaped");
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public ShapedDisplay(@NotNull RecipeHolder<ShapedRecipe> recipeHolder) {
|
||||
super(
|
||||
ofIngredient(recipeHolder.value()),
|
||||
List.of(EntryIngredient.of(recipeHolder.value().assemble(CraftingInput.EMPTY, CraftRegistry.getMinecraftRegistry()))),
|
||||
recipeHolder.id().location()
|
||||
);
|
||||
this.width = recipeHolder.value().getWidth();
|
||||
this.height = recipeHolder.value().getHeight();
|
||||
}
|
||||
|
||||
public ShapedDisplay(@NotNull ShapedCraftingRecipeDisplay recipeDisplay, ResourceLocation id) {
|
||||
super(
|
||||
Display.ofSlotDisplays(recipeDisplay.ingredients()),
|
||||
List.of(ofSlotDisplay(recipeDisplay.result())),
|
||||
id
|
||||
);
|
||||
this.width = recipeDisplay.width();
|
||||
this.height = recipeDisplay.height();
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static CraftingDisplay of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location, int width, int height) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static List<EntryIngredient> ofIngredient(ShapedRecipe recipe) {
|
||||
return recipe.getIngredients().stream().map(ingredient -> {
|
||||
if (ingredient.isEmpty()) {
|
||||
return EntryIngredient.empty();
|
||||
}
|
||||
ItemStack[] itemStacks = ingredient.get().items()
|
||||
.map(itemHolder -> new ItemStack(itemHolder, 1))
|
||||
.toArray(ItemStack[]::new);
|
||||
return EntryIngredient.of(itemStacks);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
|
||||
public StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
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.crafting.CraftingInput;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||
import net.minecraft.world.item.crafting.display.ShapelessCraftingRecipeDisplay;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ShapelessDisplay extends CraftingDisplay {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CraftingDisplay::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
CraftingDisplay::getOutputEntries,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
CraftingDisplay::getOptionalLocation,
|
||||
ShapelessDisplay::of
|
||||
);
|
||||
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/crafting/shapeless");
|
||||
|
||||
public ShapelessDisplay(@NotNull List<EntryIngredient> inputs,
|
||||
@NotNull List<EntryIngredient> outputs,
|
||||
@NotNull ResourceLocation location) {
|
||||
super(inputs, outputs, location);
|
||||
}
|
||||
|
||||
public ShapelessDisplay(@NotNull RecipeHolder<ShapelessRecipe> recipeHolder) {
|
||||
this(
|
||||
recipeHolder.value().placementInfo().ingredients().stream().map(EntryIngredient::ofIngredient).toList(),
|
||||
List.of(EntryIngredient.of(recipeHolder.value().assemble(CraftingInput.EMPTY, CraftRegistry.getMinecraftRegistry()))),
|
||||
recipeHolder.id().location()
|
||||
);
|
||||
}
|
||||
|
||||
public ShapelessDisplay(@NotNull ShapelessCraftingRecipeDisplay recipeDisplay, ResourceLocation id) {
|
||||
this(
|
||||
ofSlotDisplays(recipeDisplay.ingredients()),
|
||||
List.of(ofSlotDisplay(recipeDisplay.result())),
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static CraftingDisplay of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return getInputEntries().size() > 4 ? 3 : 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return getInputEntries().size() > 4 ? 3 : 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
|
||||
public StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.SmeltingRecipe;
|
||||
|
||||
public class SmeltingDisplay extends CookingDisplay {
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smelting");
|
||||
|
||||
public SmeltingDisplay(RecipeHolder<SmeltingRecipe> recipe) {
|
||||
super(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.core.Holder;
|
||||
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.util.ByIdMap;
|
||||
import net.minecraft.world.item.equipment.trim.TrimPattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class SmithingDisplay extends Display {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, SmithingDisplay> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
SmithingDisplay::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
SmithingDisplay::getOutputEntries,
|
||||
ByteBufCodecs.optional(SmithingRecipeType.STREAM_CODEC),
|
||||
SmithingDisplay::getOptionalType,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
SmithingDisplay::getOptionalLocation,
|
||||
SmithingDisplay::of
|
||||
);
|
||||
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smithing");
|
||||
|
||||
private final SmithingRecipeType type;
|
||||
|
||||
public SmithingDisplay(
|
||||
@NotNull List<EntryIngredient> inputs,
|
||||
@NotNull List<EntryIngredient> outputs,
|
||||
@NotNull SmithingRecipeType type,
|
||||
@NotNull ResourceLocation location
|
||||
) {
|
||||
super(inputs, outputs, location);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static SmithingDisplay of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<SmithingRecipeType> type, Optional<ResourceLocation> location) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Optional<SmithingRecipeType> getOptionalType() {
|
||||
return Optional.of(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ? extends Display> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
public enum SmithingRecipeType {
|
||||
TRIM,
|
||||
TRANSFORM,
|
||||
;
|
||||
|
||||
public static final Codec<SmithingRecipeType> CODEC = Codec.STRING.xmap(SmithingRecipeType::valueOf, SmithingRecipeType::name);
|
||||
public static final IntFunction<SmithingRecipeType> BY_ID = ByIdMap.continuous(Enum::ordinal, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
|
||||
public static final StreamCodec<ByteBuf, SmithingRecipeType> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, Enum::ordinal);
|
||||
}
|
||||
|
||||
public static class Trimming extends SmithingDisplay {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, SmithingDisplay.Trimming> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
SmithingDisplay.Trimming::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
SmithingDisplay.Trimming::getOutputEntries,
|
||||
ByteBufCodecs.optional(SmithingRecipeType.STREAM_CODEC),
|
||||
SmithingDisplay.Trimming::getOptionalType,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
SmithingDisplay.Trimming::getOptionalLocation,
|
||||
TrimPattern.STREAM_CODEC,
|
||||
SmithingDisplay.Trimming::pattern,
|
||||
SmithingDisplay.Trimming::of
|
||||
);
|
||||
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smithing/trimming");
|
||||
|
||||
private final Holder<TrimPattern> pattern;
|
||||
|
||||
public Trimming(
|
||||
@NotNull List<EntryIngredient> inputs,
|
||||
@NotNull List<EntryIngredient> outputs,
|
||||
@NotNull SmithingRecipeType type,
|
||||
@NotNull ResourceLocation location,
|
||||
@NotNull Holder<TrimPattern> pattern
|
||||
) {
|
||||
super(inputs, outputs, type, location);
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Trimming of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<SmithingRecipeType> type, Optional<ResourceLocation> location, Holder<TrimPattern> pattern) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, ? extends Display> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
public Holder<TrimPattern> pattern() {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.crafting.AbstractCookingRecipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
|
||||
public class SmokingDisplay extends CookingDisplay {
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smoking");
|
||||
|
||||
public SmokingDisplay(RecipeHolder<? extends AbstractCookingRecipe> recipe) {
|
||||
super(recipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.display;
|
||||
|
||||
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.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.item.crafting.SingleRecipeInput;
|
||||
import net.minecraft.world.item.crafting.StonecutterRecipe;
|
||||
import org.bukkit.craftbukkit.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* see me.shedaniel.rei.plugin.common.displays.DefaultStoneCuttingDisplay
|
||||
*/
|
||||
public class StoneCuttingDisplay extends Display {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, StoneCuttingDisplay> CODEC = StreamCodec.composite(
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
StoneCuttingDisplay::getInputEntries,
|
||||
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||
StoneCuttingDisplay::getOutputEntries,
|
||||
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||
StoneCuttingDisplay::getOptionalLocation,
|
||||
StoneCuttingDisplay::of
|
||||
);
|
||||
|
||||
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/stone_cutting");
|
||||
|
||||
public StoneCuttingDisplay(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull ResourceLocation id) {
|
||||
super(inputs, outputs, id);
|
||||
}
|
||||
|
||||
public StoneCuttingDisplay(RecipeHolder<StonecutterRecipe> recipeHolder) {
|
||||
this(
|
||||
List.of(EntryIngredient.ofIngredient(recipeHolder.value().input())),
|
||||
List.of(EntryIngredient.of(recipeHolder.value().assemble(new SingleRecipeInput(ItemStack.EMPTY), CraftRegistry.getMinecraftRegistry()))),
|
||||
recipeHolder.id().location()
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private static StoneCuttingDisplay of(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull Optional<ResourceLocation> id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getSerializerId() {
|
||||
return SERIALIZER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamCodec<RegistryFriendlyByteBuf, StoneCuttingDisplay> streamCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.ingredient;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
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.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class EntryIngredient {
|
||||
private static final StreamCodec<RegistryFriendlyByteBuf, Holder<Item>> ITEM_STREAM_CODEC = ByteBufCodecs.holderRegistry(Registries.ITEM);
|
||||
private static final ResourceLocation ITEM_ID = ResourceLocation.withDefaultNamespace("item");
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, EntryIngredient> CODEC = new StreamCodec<>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public EntryIngredient decode(@NotNull RegistryFriendlyByteBuf buffer) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(@NotNull RegistryFriendlyByteBuf buffer, @NotNull EntryIngredient value) {
|
||||
ByteBufCodecs.writeCount(buffer, value.size(), Integer.MAX_VALUE);
|
||||
value.stream().forEach(itemStack -> {
|
||||
buffer.writeResourceLocation(ITEM_ID);
|
||||
if (itemStack.isEmpty()) {
|
||||
buffer.writeVarInt(0);
|
||||
} else {
|
||||
buffer.writeVarInt(itemStack.getCount());
|
||||
ITEM_STREAM_CODEC.encode(buffer, itemStack.getItemHolder());
|
||||
DataComponentPatch.STREAM_CODEC.encode(buffer, itemStack.components.asPatch());
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
private static final EntryIngredient EMPTY = new EntryIngredient(new ItemStack[0]);
|
||||
@NotNull
|
||||
private final ItemStack[] array;
|
||||
|
||||
private EntryIngredient(@NotNull ItemStack[] array) {
|
||||
this.array = Objects.requireNonNull(array);
|
||||
}
|
||||
|
||||
public static EntryIngredient empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
public static EntryIngredient ofItemHolder(@NotNull Holder<? extends ItemLike> item) {
|
||||
return EntryIngredient.of(item.value());
|
||||
}
|
||||
|
||||
public static EntryIngredient of(@NotNull ItemLike item) {
|
||||
return EntryIngredient.of(new ItemStack(item));
|
||||
}
|
||||
|
||||
public static EntryIngredient of(@NotNull ItemStack itemStack) {
|
||||
return new EntryIngredient(new ItemStack[]{itemStack});
|
||||
}
|
||||
|
||||
public static EntryIngredient of(@NotNull ItemStack... itemStacks) {
|
||||
return new EntryIngredient(Arrays.copyOf(itemStacks, itemStacks.length));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static EntryIngredient ofIngredient(Ingredient ingredient) {
|
||||
if (ingredient.isEmpty()) {
|
||||
return EntryIngredient.empty();
|
||||
}
|
||||
ItemStack[] itemStacks = ingredient.items()
|
||||
.map(itemHolder -> itemHolder.value().getDefaultInstance())
|
||||
.toArray(ItemStack[]::new);
|
||||
return EntryIngredient.of(itemStacks);
|
||||
}
|
||||
|
||||
public Stream<ItemStack> stream() {
|
||||
return Arrays.stream(array);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return array.length == 0;
|
||||
}
|
||||
|
||||
public ItemStack get(int index) {
|
||||
return array[index].copy();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return array.length;
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.payload;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.util.ByIdMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.rei.display.Display;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
// This payload will never be sent to the client. We use PacketTransformer to send split payload.
|
||||
public record DisplaySyncPayload(
|
||||
SyncType syncType,
|
||||
Collection<Display> displays,
|
||||
long version
|
||||
) implements LeavesCustomPayload {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static final StreamCodec<? super RegistryFriendlyByteBuf, DisplaySyncPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
SyncType.STREAM_CODEC,
|
||||
DisplaySyncPayload::syncType,
|
||||
Display.dispatchCodec().apply(codec -> new StreamCodec<RegistryFriendlyByteBuf, Display>() {
|
||||
@Override
|
||||
public void encode(@NotNull RegistryFriendlyByteBuf buf, @NotNull Display display) {
|
||||
RegistryFriendlyByteBuf tmpBuf = new RegistryFriendlyByteBuf(Unpooled.buffer(), buf.registryAccess());
|
||||
try {
|
||||
codec.encode(tmpBuf, display);
|
||||
} catch (Exception e) {
|
||||
tmpBuf.release();
|
||||
buf.writeBoolean(false);
|
||||
LOGGER.warn("Failed to encode display: " + display, e);
|
||||
return;
|
||||
}
|
||||
buf.writeBoolean(true);
|
||||
RegistryFriendlyByteBuf.writeByteArray(buf, ByteBufUtil.getBytes(tmpBuf));
|
||||
tmpBuf.release();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Display decode(@NotNull RegistryFriendlyByteBuf buf) {
|
||||
// The DisplayDecoder will not be called on the server side
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
).apply(ByteBufCodecs.<RegistryFriendlyByteBuf, Display, Collection<Display>>collection(ArrayList::new)).map(
|
||||
collection -> collection.stream().filter(Objects::nonNull).toList(),
|
||||
UnaryOperator.identity()
|
||||
),
|
||||
DisplaySyncPayload::displays,
|
||||
ByteBufCodecs.LONG,
|
||||
DisplaySyncPayload::version,
|
||||
DisplaySyncPayload::new
|
||||
);
|
||||
|
||||
|
||||
public enum SyncType {
|
||||
APPEND,
|
||||
SET;
|
||||
|
||||
public static final IntFunction<SyncType> BY_ID = ByIdMap.continuous(Enum::ordinal, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
|
||||
public static final StreamCodec<ByteBuf, SyncType> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, Enum::ordinal);
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.SlotAccessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class InputSlotCrafter<T extends AbstractContainerMenu> {
|
||||
protected T container;
|
||||
private Iterable<SlotAccessor> inputStacks;
|
||||
private Iterable<SlotAccessor> inventoryStacks;
|
||||
protected ServerPlayer player;
|
||||
|
||||
protected InputSlotCrafter(T container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public void fillInputSlots(ServerPlayer player, boolean hasShift) {
|
||||
this.player = player;
|
||||
this.inventoryStacks = this.getInventorySlots();
|
||||
this.inputStacks = this.getInputSlots();
|
||||
|
||||
// Return the already placed items on the grid
|
||||
this.cleanInputs();
|
||||
|
||||
ItemRecipeFinder recipeFinder = new ItemRecipeFinder();
|
||||
this.populateRecipeFinder(recipeFinder);
|
||||
List<List<ItemStack>> ingredients = new ArrayList<>(this.getInputs());
|
||||
|
||||
if (recipeFinder.findRecipe(ingredients, 1, null)) {
|
||||
this.fillInputSlots(recipeFinder, ingredients, hasShift);
|
||||
} else {
|
||||
this.cleanInputs();
|
||||
this.markDirty();
|
||||
throw new NotEnoughMaterialsException();
|
||||
}
|
||||
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
protected abstract Iterable<SlotAccessor> getInputSlots();
|
||||
|
||||
protected abstract Iterable<SlotAccessor> getInventorySlots();
|
||||
|
||||
protected abstract List<List<ItemStack>> getInputs();
|
||||
|
||||
protected abstract void populateRecipeFinder(ItemRecipeFinder recipeFinder);
|
||||
|
||||
protected abstract void markDirty();
|
||||
|
||||
public void alignRecipeToGrid(Iterable<SlotAccessor> inputStacks, Iterator<ItemStack> recipeItems, int craftsAmount) {
|
||||
for (SlotAccessor inputStack : inputStacks) {
|
||||
if (!recipeItems.hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.acceptAlignedInput(recipeItems.next(), inputStack, craftsAmount);
|
||||
}
|
||||
}
|
||||
|
||||
public void acceptAlignedInput(ItemStack toBeTakenStack, SlotAccessor inputStack, int craftsAmount) {
|
||||
if (!toBeTakenStack.isEmpty()) {
|
||||
for (int i = 0; i < craftsAmount; ++i) {
|
||||
this.fillInputSlot(inputStack, toBeTakenStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void fillInputSlot(SlotAccessor slot, ItemStack toBeTakenStack) {
|
||||
SlotAccessor takenSlot = this.takeInventoryStack(toBeTakenStack);
|
||||
if (takenSlot != null) {
|
||||
ItemStack takenStack = takenSlot.getItemStack().copy();
|
||||
if (!takenStack.isEmpty()) {
|
||||
if (takenStack.getCount() > 1) {
|
||||
takenSlot.takeStack(1);
|
||||
} else {
|
||||
takenSlot.setItemStack(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
takenStack.setCount(1);
|
||||
if (!slot.canPlace(takenStack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot.getItemStack().isEmpty()) {
|
||||
slot.setItemStack(takenStack);
|
||||
} else {
|
||||
slot.getItemStack().grow(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void fillInputSlots(ItemRecipeFinder recipeFinder, List<List<ItemStack>> ingredients, boolean hasShift) {
|
||||
int recipeCrafts = recipeFinder.countRecipeCrafts(ingredients, Integer.MAX_VALUE, null);
|
||||
int amountToFill = hasShift ? recipeCrafts : 1;
|
||||
List<ItemStack> recipeItems = new ArrayList<>();
|
||||
if (recipeFinder.findRecipe(ingredients, amountToFill, recipeItems::add)) {
|
||||
int finalCraftsAmount = amountToFill;
|
||||
|
||||
for (ItemStack itemId : recipeItems) {
|
||||
// Fix issue with empty item id (grid slot) [shift-click issue]
|
||||
if (itemId.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
finalCraftsAmount = Math.min(finalCraftsAmount, itemId.getMaxStackSize());
|
||||
}
|
||||
|
||||
recipeItems.clear();
|
||||
|
||||
if (recipeFinder.findRecipe(ingredients, finalCraftsAmount, recipeItems::add)) {
|
||||
this.cleanInputs();
|
||||
this.alignRecipeToGrid(inputStacks, recipeItems.iterator(), finalCraftsAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void cleanInputs();
|
||||
|
||||
@Nullable
|
||||
public SlotAccessor takeInventoryStack(ItemStack itemStack) {
|
||||
boolean rejectedModification = false;
|
||||
for (SlotAccessor inventoryStack : inventoryStacks) {
|
||||
ItemStack itemStack1 = inventoryStack.getItemStack();
|
||||
if (!itemStack1.isEmpty() && areItemsEqual(itemStack, itemStack1) && !itemStack1.isDamaged() && !itemStack1.isEnchanted() && !itemStack1.has(DataComponents.CUSTOM_NAME)) {
|
||||
if (!inventoryStack.allowModification(player)) {
|
||||
rejectedModification = true;
|
||||
} else {
|
||||
return inventoryStack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rejectedModification) {
|
||||
throw new IllegalStateException("Unable to take item from inventory due to slot not allowing modification! Item requested: " + itemStack);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean areItemsEqual(ItemStack stack1, ItemStack stack2) {
|
||||
return ItemStack.isSameItemSameComponents(stack1, stack2);
|
||||
}
|
||||
|
||||
public static class NotEnoughMaterialsException extends RuntimeException {
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ItemRecipeFinder {
|
||||
private final Interner<ItemKey> keys = Interners.newWeakInterner();
|
||||
private final RecipeFinder<ItemKey, Ingredient> finder = new RecipeFinder<>();
|
||||
|
||||
public boolean contains(ItemStack item) {
|
||||
return finder.contains(ofKey(item));
|
||||
}
|
||||
|
||||
public void take(ItemStack item, int amount) {
|
||||
finder.take(ofKey(item), amount);
|
||||
}
|
||||
|
||||
public void put(ItemStack item, int amount) {
|
||||
finder.put(ofKey(item), amount);
|
||||
}
|
||||
|
||||
public void addNormalItem(ItemStack itemStack) {
|
||||
if (Inventory.isUsableForCrafting(itemStack)) {
|
||||
this.addItem(itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(ItemStack itemStack) {
|
||||
this.addItem(itemStack, itemStack.getMaxStackSize());
|
||||
}
|
||||
|
||||
public void addItem(ItemStack itemStack, int i) {
|
||||
if (!itemStack.isEmpty()) {
|
||||
int j = Math.min(i, itemStack.getCount());
|
||||
this.finder.put(ofKey(itemStack), j);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean findRecipe(List<List<ItemStack>> list, int maxCrafts, @Nullable Consumer<ItemStack> output) {
|
||||
return finder.findRecipe(toIngredients(list), maxCrafts, flatten(itemStack -> {
|
||||
if (output != null) {
|
||||
output.accept(itemStack);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public int countRecipeCrafts(List<List<ItemStack>> list, int maxCrafts, @Nullable Consumer<ItemStack> output) {
|
||||
return finder.countRecipeCrafts(toIngredients(list), maxCrafts, flatten(itemStack -> {
|
||||
if (output != null) {
|
||||
output.accept(itemStack);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private ItemKey ofKey(ItemStack itemStack) {
|
||||
return keys.intern(new ItemKey(itemStack.getItemHolder(), itemStack.getComponentsPatch()));
|
||||
}
|
||||
|
||||
private Ingredient ofKeys(int index, List<ItemStack> itemStack) {
|
||||
return new Ingredient(index, itemStack.stream().map(this::ofKey).toList());
|
||||
}
|
||||
|
||||
private List<Ingredient> toIngredients(List<List<ItemStack>> list) {
|
||||
List<Ingredient> ingredients = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
List<ItemStack> stacks = list.get(i);
|
||||
if (!stacks.isEmpty()) {
|
||||
ingredients.add(ofKeys(i, stacks));
|
||||
}
|
||||
}
|
||||
|
||||
return ingredients;
|
||||
}
|
||||
|
||||
private static BiConsumer<ItemKey, Ingredient> flatten(Consumer<ItemStack> consumer) {
|
||||
int[] lastIndex = {-1};
|
||||
return (itemKey, ingredient) -> {
|
||||
for (int i = lastIndex[0] + 1; i < ingredient.index(); i++) {
|
||||
consumer.accept(ItemStack.EMPTY);
|
||||
}
|
||||
consumer.accept(new ItemStack(itemKey.item(), 1, itemKey.patch()));
|
||||
lastIndex[0] = ingredient.index();
|
||||
};
|
||||
}
|
||||
|
||||
private record Ingredient(int index, List<ItemKey> elements) implements RecipeFinder.Ingredient<ItemKey> {
|
||||
}
|
||||
|
||||
private record ItemKey(Holder<Item> item, DataComponentPatch patch) {
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.SlotAccessor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class NewInputSlotCrafter<T extends AbstractContainerMenu> extends InputSlotCrafter<T> {
|
||||
protected final List<SlotAccessor> inputSlots;
|
||||
protected final List<SlotAccessor> inventorySlots;
|
||||
protected final List<List<ItemStack>> inputs;
|
||||
|
||||
public NewInputSlotCrafter(T container, List<SlotAccessor> inputSlots, List<SlotAccessor> inventorySlots, List<List<ItemStack>> inputs) {
|
||||
super(container);
|
||||
this.inputSlots = inputSlots;
|
||||
this.inventorySlots = inventorySlots;
|
||||
this.inputs = inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<SlotAccessor> getInputSlots() {
|
||||
return this.inputSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<SlotAccessor> getInventorySlots() {
|
||||
return this.inventorySlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<List<ItemStack>> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populateRecipeFinder(ItemRecipeFinder recipeFinder) {
|
||||
for (SlotAccessor slot : getInventorySlots()) {
|
||||
recipeFinder.addNormalItem(slot.getItemStack());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markDirty() {
|
||||
player.getInventory().setChanged();
|
||||
container.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanInputs() {
|
||||
for (SlotAccessor slot : getInputSlots()) {
|
||||
org.bukkit.inventory.ItemStack bukkitStack = slot.getItemStack().getBukkitStack();
|
||||
if (bukkitStack.getType().isAir()) {
|
||||
continue;
|
||||
}
|
||||
HashMap<Integer, org.bukkit.inventory.ItemStack> notAdded = player.getBukkitEntity().getInventory().addItem(bukkitStack);
|
||||
if (notAdded.isEmpty()) {
|
||||
slot.setItemStack(ItemStack.EMPTY);
|
||||
} else {
|
||||
org.bukkit.inventory.ItemStack remain = notAdded.values().iterator().next();
|
||||
slot.setItemStack(ItemStack.fromBukkitCopy(remain));
|
||||
throw new IllegalStateException("rei.rei.no.slot.in.inv");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+409
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class RecipeFinder<T, I extends RecipeFinder.Ingredient<T>> {
|
||||
public final Reference2IntOpenHashMap<T> amounts = new Reference2IntOpenHashMap<>();
|
||||
|
||||
public boolean contains(T item) {
|
||||
return this.amounts.getInt(item) > 0;
|
||||
}
|
||||
|
||||
boolean containsAtLeast(T object, int i) {
|
||||
return this.amounts.getInt(object) >= i;
|
||||
}
|
||||
|
||||
public void take(T item, int amount) {
|
||||
int taken = this.amounts.addTo(item, -amount);
|
||||
if (taken < amount) {
|
||||
throw new IllegalStateException("Took " + amount + " items, but only had " + taken);
|
||||
}
|
||||
}
|
||||
|
||||
public void put(T item, int amount) {
|
||||
this.amounts.addTo(item, amount);
|
||||
}
|
||||
|
||||
public boolean findRecipe(List<I> list, int maxCrafts, @Nullable BiConsumer<T, I> output) {
|
||||
return new Filter(list).tryPick(maxCrafts, output);
|
||||
}
|
||||
|
||||
public int countRecipeCrafts(List<I> list, int maxCrafts, @Nullable BiConsumer<T, I> output) {
|
||||
return new Filter(list).tryPickAll(maxCrafts, output);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.amounts.clear();
|
||||
}
|
||||
|
||||
class Filter {
|
||||
private final List<I> ingredients;
|
||||
private final int ingredientCount;
|
||||
private final List<T> items;
|
||||
private final int itemCount;
|
||||
private final BitSet data;
|
||||
private final IntList path = new IntArrayList();
|
||||
|
||||
public Filter(final List<I> list) {
|
||||
this.ingredients = list;
|
||||
this.ingredientCount = this.ingredients.size();
|
||||
this.items = this.getUniqueAvailableIngredientItems();
|
||||
this.itemCount = this.items.size();
|
||||
this.data = new BitSet(this.visitedIngredientCount() + this.visitedItemCount() + this.satisfiedCount() + this.connectionCount() + this.residualCount());
|
||||
this.setInitialConnections();
|
||||
}
|
||||
|
||||
private void setInitialConnections() {
|
||||
for (int i = 0; i < this.ingredientCount; i++) {
|
||||
List<T> list = this.ingredients.get(i).elements();
|
||||
|
||||
for (int j = 0; j < this.itemCount; j++) {
|
||||
if (list.contains(this.items.get(j))) {
|
||||
this.setConnection(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryPick(int maxCrafts, @Nullable BiConsumer<T, I> output) {
|
||||
if (maxCrafts <= 0) {
|
||||
return true;
|
||||
} else {
|
||||
int j = 0;
|
||||
|
||||
while (true) {
|
||||
IntList intList = this.tryAssigningNewItem(maxCrafts);
|
||||
if (intList == null) {
|
||||
boolean bl = j == this.ingredientCount;
|
||||
boolean bl2 = bl && output != null;
|
||||
this.clearAllVisited();
|
||||
this.clearSatisfied();
|
||||
|
||||
for (int l = 0; l < this.ingredientCount; l++) {
|
||||
for (int m = 0; m < this.itemCount; m++) {
|
||||
if (this.isAssigned(m, l)) {
|
||||
this.unassign(m, l);
|
||||
put(this.items.get(m), maxCrafts);
|
||||
if (bl2) {
|
||||
output.accept(this.items.get(m), this.ingredients.get(l));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert this.data.get(this.residualOffset(), this.residualOffset() + this.residualCount()).isEmpty();
|
||||
|
||||
return bl;
|
||||
}
|
||||
|
||||
int k = intList.getInt(0);
|
||||
take(this.items.get(k), maxCrafts);
|
||||
int l = intList.size() - 1;
|
||||
this.setSatisfied(intList.getInt(l));
|
||||
j++;
|
||||
|
||||
for (int mx = 0; mx < intList.size() - 1; mx++) {
|
||||
if (isPathIndexItem(mx)) {
|
||||
int n = intList.getInt(mx);
|
||||
int o = intList.getInt(mx + 1);
|
||||
this.assign(n, o);
|
||||
} else {
|
||||
int n = intList.getInt(mx + 1);
|
||||
int o = intList.getInt(mx);
|
||||
this.unassign(n, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPathIndexItem(int i) {
|
||||
return (i & 1) == 0;
|
||||
}
|
||||
|
||||
private List<T> getUniqueAvailableIngredientItems() {
|
||||
Set<T> set = new ReferenceOpenHashSet<>();
|
||||
|
||||
for (Ingredient<T> ingredient : this.ingredients) {
|
||||
set.addAll(ingredient.elements());
|
||||
}
|
||||
|
||||
set.removeIf(object -> !contains(object));
|
||||
return List.copyOf(set);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IntList tryAssigningNewItem(int i) {
|
||||
this.clearAllVisited();
|
||||
|
||||
for (int j = 0; j < this.itemCount; j++) {
|
||||
if (containsAtLeast(this.items.get(j), i)) {
|
||||
IntList intList = this.findNewItemAssignmentPath(j);
|
||||
if (intList != null) {
|
||||
return intList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IntList findNewItemAssignmentPath(int i) {
|
||||
this.path.clear();
|
||||
this.visitItem(i);
|
||||
this.path.add(i);
|
||||
|
||||
while (!this.path.isEmpty()) {
|
||||
int j = this.path.size();
|
||||
int k = this.path.getInt(j - 1);
|
||||
if (isPathIndexItem(j - 1)) {
|
||||
for (int l = 0; l < this.ingredientCount; l++) {
|
||||
if (!this.hasVisitedIngredient(l) && this.hasConnection(k, l) && !this.isAssigned(k, l)) {
|
||||
this.visitIngredient(l);
|
||||
this.path.add(l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!this.isSatisfied(k)) {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
for (int lx = 0; lx < this.itemCount; lx++) {
|
||||
if (!this.hasVisitedItem(lx) && this.isAssigned(lx, k)) {
|
||||
assert this.hasConnection(lx, k);
|
||||
|
||||
this.visitItem(lx);
|
||||
this.path.add(lx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int l = this.path.size();
|
||||
if (l == j) {
|
||||
this.path.removeInt(l - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int visitedIngredientOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int visitedIngredientCount() {
|
||||
return this.ingredientCount;
|
||||
}
|
||||
|
||||
private int visitedItemOffset() {
|
||||
return this.visitedIngredientOffset() + this.visitedIngredientCount();
|
||||
}
|
||||
|
||||
private int visitedItemCount() {
|
||||
return this.itemCount;
|
||||
}
|
||||
|
||||
private int satisfiedOffset() {
|
||||
return this.visitedItemOffset() + this.visitedItemCount();
|
||||
}
|
||||
|
||||
private int satisfiedCount() {
|
||||
return this.ingredientCount;
|
||||
}
|
||||
|
||||
private int connectionOffset() {
|
||||
return this.satisfiedOffset() + this.satisfiedCount();
|
||||
}
|
||||
|
||||
private int connectionCount() {
|
||||
return this.ingredientCount * this.itemCount;
|
||||
}
|
||||
|
||||
private int residualOffset() {
|
||||
return this.connectionOffset() + this.connectionCount();
|
||||
}
|
||||
|
||||
private int residualCount() {
|
||||
return this.ingredientCount * this.itemCount;
|
||||
}
|
||||
|
||||
private boolean isSatisfied(int i) {
|
||||
return this.data.get(this.getSatisfiedIndex(i));
|
||||
}
|
||||
|
||||
private void setSatisfied(int i) {
|
||||
this.data.set(this.getSatisfiedIndex(i));
|
||||
}
|
||||
|
||||
private int getSatisfiedIndex(int i) {
|
||||
assert i >= 0 && i < this.ingredientCount;
|
||||
|
||||
return this.satisfiedOffset() + i;
|
||||
}
|
||||
|
||||
private void clearSatisfied() {
|
||||
this.clearRange(this.satisfiedOffset(), this.satisfiedCount());
|
||||
}
|
||||
|
||||
private void setConnection(int i, int j) {
|
||||
this.data.set(this.getConnectionIndex(i, j));
|
||||
}
|
||||
|
||||
private boolean hasConnection(int i, int j) {
|
||||
return this.data.get(this.getConnectionIndex(i, j));
|
||||
}
|
||||
|
||||
private int getConnectionIndex(int i, int j) {
|
||||
assert i >= 0 && i < this.itemCount;
|
||||
|
||||
assert j >= 0 && j < this.ingredientCount;
|
||||
|
||||
return this.connectionOffset() + i * this.ingredientCount + j;
|
||||
}
|
||||
|
||||
private boolean isAssigned(int i, int j) {
|
||||
return this.data.get(this.getResidualIndex(i, j));
|
||||
}
|
||||
|
||||
private void assign(int i, int j) {
|
||||
int k = this.getResidualIndex(i, j);
|
||||
|
||||
assert !this.data.get(k);
|
||||
|
||||
this.data.set(k);
|
||||
}
|
||||
|
||||
private void unassign(int i, int j) {
|
||||
int k = this.getResidualIndex(i, j);
|
||||
|
||||
assert this.data.get(k);
|
||||
|
||||
this.data.clear(k);
|
||||
}
|
||||
|
||||
private int getResidualIndex(int i, int j) {
|
||||
assert i >= 0 && i < this.itemCount;
|
||||
|
||||
assert j >= 0 && j < this.ingredientCount;
|
||||
|
||||
return this.residualOffset() + i * this.ingredientCount + j;
|
||||
}
|
||||
|
||||
private void visitIngredient(int i) {
|
||||
this.data.set(this.getVisitedIngredientIndex(i));
|
||||
}
|
||||
|
||||
private boolean hasVisitedIngredient(int i) {
|
||||
return this.data.get(this.getVisitedIngredientIndex(i));
|
||||
}
|
||||
|
||||
private int getVisitedIngredientIndex(int i) {
|
||||
assert i >= 0 && i < this.ingredientCount;
|
||||
|
||||
return this.visitedIngredientOffset() + i;
|
||||
}
|
||||
|
||||
private void visitItem(int i) {
|
||||
this.data.set(this.getVisitiedItemIndex(i));
|
||||
}
|
||||
|
||||
private boolean hasVisitedItem(int i) {
|
||||
return this.data.get(this.getVisitiedItemIndex(i));
|
||||
}
|
||||
|
||||
private int getVisitiedItemIndex(int i) {
|
||||
assert i >= 0 && i < this.itemCount;
|
||||
|
||||
return this.visitedItemOffset() + i;
|
||||
}
|
||||
|
||||
private void clearAllVisited() {
|
||||
this.clearRange(this.visitedIngredientOffset(), this.visitedIngredientCount());
|
||||
this.clearRange(this.visitedItemOffset(), this.visitedItemCount());
|
||||
}
|
||||
|
||||
private void clearRange(int i, int j) {
|
||||
this.data.clear(i, i + j);
|
||||
}
|
||||
|
||||
public int tryPickAll(int i, @Nullable BiConsumer<T, I> output) {
|
||||
int j = 0;
|
||||
int k = Math.min(i, this.getMinIngredientCount()) + 1;
|
||||
|
||||
while (true) {
|
||||
int l = (j + k) / 2;
|
||||
if (this.tryPick(l, null)) {
|
||||
if (k - j <= 1) {
|
||||
if (l > 0) {
|
||||
this.tryPick(l, output);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
j = l;
|
||||
} else {
|
||||
k = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getMinIngredientCount() {
|
||||
int i = Integer.MAX_VALUE;
|
||||
|
||||
for (Ingredient<T> ingredient : this.ingredients) {
|
||||
int j = 0;
|
||||
|
||||
for (T object : ingredient.elements()) {
|
||||
j = Math.max(j, amounts.getInt(object));
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
i = Math.min(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Ingredient<T> {
|
||||
List<T> elements();
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer.slot;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class PlayerInventorySlotAccessor implements SlotAccessor {
|
||||
protected Player player;
|
||||
protected int index;
|
||||
|
||||
public PlayerInventorySlotAccessor(Player player, int index) {
|
||||
this.player = player;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack() {
|
||||
return player.getInventory().getItem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemStack(ItemStack stack) {
|
||||
this.player.getInventory().setItem(index, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeStack(int amount) {
|
||||
this.player.getInventory().removeItem(index, amount);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer.slot;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public interface SlotAccessor {
|
||||
ItemStack getItemStack();
|
||||
|
||||
void setItemStack(ItemStack stack);
|
||||
|
||||
void takeStack(int amount);
|
||||
|
||||
default boolean allowModification(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean canPlace(ItemStack stack) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer.slot;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class VanillaSlotAccessor implements SlotAccessor {
|
||||
protected Slot slot;
|
||||
|
||||
public VanillaSlotAccessor(Slot slot) {
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack() {
|
||||
return slot.getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemStack(ItemStack stack) {
|
||||
this.slot.set(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeStack(int amount) {
|
||||
slot.remove(amount);
|
||||
}
|
||||
|
||||
public Slot getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowModification(Player player) {
|
||||
return slot.allowModification(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlace(ItemStack stack) {
|
||||
return slot.mayPlace(stack);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
package org.leavesmc.leaves.protocol.servux;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.function.ServuxProtocolConfig;
|
||||
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package org.leavesmc.leaves.protocol.servux;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import fun.bm.lophine.config.modules.function.ServuxProtocolConfig;
|
||||
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
package org.leavesmc.leaves.protocol.servux;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.function.ServuxProtocolConfig;
|
||||
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.leavesmc.leaves.protocol.servux.litematics;
|
||||
|
||||
import fun.bm.lophine.config.modules.function.ServuxProtocolConfig;
|
||||
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
package org.leavesmc.leaves.protocol.servux.litematics.placement;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import fun.bm.lophine.config.modules.function.ServuxProtocolConfig;
|
||||
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
|
||||
import io.papermc.paper.threadedregions.RegionizedServer;
|
||||
import me.earthme.luminol.utils.NullPlugin;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import fun.bm.lophine.config.modules.function.protocol.SyncmaticaProtocolConfig;
|
||||
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.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
@LeavesProtocol.Register(namespace = "syncmatica")
|
||||
public class CommunicationManager implements LeavesProtocol {
|
||||
|
||||
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();
|
||||
private static final Map<UUID, List<ServerPlacement>> downloadingFile = new HashMap<>();
|
||||
private static final Map<ExchangeTarget, ServerPlayer> playerMap = new HashMap<>();
|
||||
|
||||
public CommunicationManager() {
|
||||
}
|
||||
|
||||
public static GameProfile getGameProfile(final ExchangeTarget exchangeTarget) {
|
||||
return playerMap.get(exchangeTarget).getGameProfile();
|
||||
}
|
||||
|
||||
@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)
|
||||
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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return SyncmaticaProtocolConfig.enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class FeatureSet {
|
||||
|
||||
private static final Map<String, FeatureSet> versionFeatures;
|
||||
|
||||
static {
|
||||
versionFeatures = new HashMap<>();
|
||||
versionFeatures.put("0.1", new FeatureSet(Collections.singletonList(Feature.CORE)));
|
||||
}
|
||||
|
||||
private final Collection<Feature> features;
|
||||
|
||||
public FeatureSet(final Collection<Feature> features) {
|
||||
this.features = 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 boolean hasFeature(final Feature f) {
|
||||
return features.contains(f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.syncmatica;
|
||||
|
||||
public enum MessageType {
|
||||
SUCCESS,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user