mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
665 lines
30 KiB
Diff
665 lines
30 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
|
Date: Sat, 3 Dec 2022 08:57:15 +0800
|
|
Subject: [PATCH] Jade Protocol
|
|
|
|
This patch is Powered by Jade(https://github.com/Snownee/Jade)
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
index d81d6697fd1524d73df631b76c39cf363e1c33ec..53279da009fb9f203d7e8006cb1ef14d9ef8c017 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
@@ -3560,6 +3560,9 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
|
if (top.leavesmc.leaves.LeavesConfig.bborProtocol && packet.identifier.equals(top.leavesmc.leaves.protocol.BBORProtocol.SUBSCRIBE)) {
|
|
top.leavesmc.leaves.protocol.BBORProtocol.onPlayerSubscribed(player);
|
|
}
|
|
+ if (top.leavesmc.leaves.LeavesConfig.jadeProtocol && ProtocolUtils.isNamespacePacket(packet, top.leavesmc.leaves.protocol.JadeProtocol.PROTOCOL_ID)) {
|
|
+ top.leavesmc.leaves.protocol.JadeProtocol.handlePacket(server, player, packet);
|
|
+ }
|
|
} catch (Exception ex) {
|
|
ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex);
|
|
this.disconnect("Invalid custom payload!", org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PAYLOAD);
|
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
index 50833a1db2728131b6638a40236b81a219ed5f53..aaa48a93550b6ad7bf5c904491e5d34d4b7dcceb 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -336,6 +336,7 @@ public abstract class PlayerList {
|
|
// Leaves end - bot support
|
|
top.leavesmc.leaves.protocol.PcaSyncProtocol.onJoin(player); // Leaves - pca
|
|
top.leavesmc.leaves.protocol.BBORProtocol.onPlayerLoggedIn(player); // Leaves - bbor
|
|
+ top.leavesmc.leaves.protocol.JadeProtocol.onPlayerJoin(player); // Leaves - Jade
|
|
|
|
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
index 9058f9f2e561cda9f475f33218bf7a78297de4bc..196adc124a06e7c7712e4f70fada6d50a4a56d17 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java
|
|
@@ -253,7 +253,7 @@ public class Tadpole extends AbstractFish {
|
|
|
|
}
|
|
|
|
- private int getTicksLeftUntilAdult() {
|
|
+ public int getTicksLeftUntilAdult() { // Leaves - private -> public
|
|
return Math.max(0, Tadpole.ticksToBeFrog - this.age);
|
|
}
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 0f639571f63adad7d2a738b7ce8dc0158cdeb0c6..9db4ac8fb3e00e6b37d55c5c6bac9557bae105ab 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -387,6 +387,7 @@ public final class CraftServer implements Server {
|
|
}
|
|
datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
|
|
top.leavesmc.leaves.protocol.PcaSyncProtocol.init(); // Leaves - pca
|
|
+ top.leavesmc.leaves.protocol.JadeProtocol.init(); // Leaves - Jade
|
|
}
|
|
|
|
public boolean getCommandBlockOverride(String command) {
|
|
@@ -977,6 +978,11 @@ public final class CraftServer implements Server {
|
|
top.leavesmc.leaves.protocol.BBORProtocol.loggedOutAllPlayer();
|
|
}
|
|
// Leaves end - bbor
|
|
+ // Leaves start - Jade
|
|
+ if (top.leavesmc.leaves.LeavesConfig.jadeProtocol) {
|
|
+ top.leavesmc.leaves.protocol.JadeProtocol.enableAllPlayer();
|
|
+ }
|
|
+ // Leaves end - Jade
|
|
for (ServerLevel world : this.console.getAllLevels()) {
|
|
// world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
|
|
world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
|
|
diff --git a/src/main/java/top/leavesmc/leaves/LeavesConfig.java b/src/main/java/top/leavesmc/leaves/LeavesConfig.java
|
|
index af3da4fa3073123416cc5ef37ca4db8b102b26eb..51baa0305dda36fec8855021b56867c2c863ba79 100644
|
|
--- a/src/main/java/top/leavesmc/leaves/LeavesConfig.java
|
|
+++ b/src/main/java/top/leavesmc/leaves/LeavesConfig.java
|
|
@@ -420,6 +420,11 @@ public final class LeavesConfig {
|
|
flattenTriangularDistribution = getBoolean("settings.modify.flatten-triangular-distribution", flattenTriangularDistribution);
|
|
}
|
|
|
|
+ public static boolean jadeProtocol = false;
|
|
+ private static void jadeProtocol() {
|
|
+ jadeProtocol = getBoolean("settings.protocol.jade-protocol", jadeProtocol);
|
|
+ }
|
|
+
|
|
public static final class WorldConfig {
|
|
|
|
public final String worldName;
|
|
diff --git a/src/main/java/top/leavesmc/leaves/protocol/JadeProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/JadeProtocol.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..569ccfc9ac059abdc5690e686889ed03047c2517
|
|
--- /dev/null
|
|
+++ b/src/main/java/top/leavesmc/leaves/protocol/JadeProtocol.java
|
|
@@ -0,0 +1,572 @@
|
|
+package top.leavesmc.leaves.protocol;
|
|
+
|
|
+import com.google.common.cache.Cache;
|
|
+import com.google.common.cache.CacheBuilder;
|
|
+import com.google.common.collect.ArrayListMultimap;
|
|
+import com.google.common.collect.ImmutableList;
|
|
+import com.google.common.collect.ListMultimap;
|
|
+import com.google.common.collect.Lists;
|
|
+import com.google.common.collect.Streams;
|
|
+import com.mojang.authlib.GameProfile;
|
|
+import io.netty.buffer.Unpooled;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.Registry;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.nbt.ListTag;
|
|
+import net.minecraft.network.FriendlyByteBuf;
|
|
+import net.minecraft.network.chat.Component;
|
|
+import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
|
|
+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.util.Mth;
|
|
+import net.minecraft.world.Container;
|
|
+import net.minecraft.world.LockCode;
|
|
+import net.minecraft.world.Nameable;
|
|
+import net.minecraft.world.effect.MobEffectCategory;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import net.minecraft.world.entity.AgeableMob;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntityType;
|
|
+import net.minecraft.world.entity.LivingEntity;
|
|
+import net.minecraft.world.entity.OwnableEntity;
|
|
+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.frog.Tadpole;
|
|
+import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
|
+import net.minecraft.world.entity.vehicle.ContainerEntity;
|
|
+import net.minecraft.world.item.ItemStack;
|
|
+import net.minecraft.world.item.Items;
|
|
+import net.minecraft.world.level.BaseCommandBlock;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.ChestBlock;
|
|
+import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.BlockEntity;
|
|
+import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
+import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.CommandBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.ComparatorBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.HopperBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.JukeboxBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
|
+import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
|
+import org.apache.commons.lang3.mutable.MutableInt;
|
|
+import org.apache.logging.log4j.util.TriConsumer;
|
|
+import org.jetbrains.annotations.Contract;
|
|
+import org.jetbrains.annotations.NotNull;
|
|
+import org.jetbrains.annotations.Nullable;
|
|
+import top.leavesmc.leaves.LeavesConfig;
|
|
+import top.leavesmc.leaves.LeavesLogger;
|
|
+import top.leavesmc.leaves.util.ProtocolUtils;
|
|
+
|
|
+import java.util.Collection;
|
|
+import java.util.HashMap;
|
|
+import java.util.List;
|
|
+import java.util.Locale;
|
|
+import java.util.Map;
|
|
+import java.util.Objects;
|
|
+import java.util.UUID;
|
|
+import java.util.concurrent.ExecutionException;
|
|
+import java.util.function.Function;
|
|
+import java.util.stream.IntStream;
|
|
+import java.util.stream.Stream;
|
|
+
|
|
+public class JadeProtocol {
|
|
+
|
|
+ public static final String PROTOCOL_ID = "jade";
|
|
+
|
|
+ // send
|
|
+ public static final ResourceLocation PACKET_SERVER_PING = id("server_ping");
|
|
+ public static final ResourceLocation PACKET_RECEIVE_DATA = id("receive_data");
|
|
+ // call
|
|
+ public static final ResourceLocation PACKET_REQUEST_ENTITY = id("request_entity");
|
|
+ public static final ResourceLocation PACKET_REQUEST_TILE = id("request_tile");
|
|
+
|
|
+ private static final HierarchyLookup<IJadeProvider<Entity>> entityDataProviders = new HierarchyLookup<>(Entity.class);
|
|
+ private static final HierarchyLookup<IJadeProvider<BlockEntity>> tileDataProviders = new HierarchyLookup<>(BlockEntity.class);
|
|
+
|
|
+ public static final int MAX_DISTANCE_SQR = 900;
|
|
+
|
|
+ @Contract("_ -> new")
|
|
+ public static @NotNull ResourceLocation id(String path) {
|
|
+ return new ResourceLocation(PROTOCOL_ID, path);
|
|
+ }
|
|
+
|
|
+ public static void onPlayerJoin(ServerPlayer player) {
|
|
+ if (LeavesConfig.jadeProtocol) {
|
|
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
|
|
+ buf.writeUtf("{}");
|
|
+ ProtocolUtils.sendPayloadPacket(player, PACKET_SERVER_PING, buf);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void enableAllPlayer() {
|
|
+ for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().players) {
|
|
+ onPlayerJoin(player);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final Map<ResourceLocation, TriConsumer<MinecraftServer, ServerPlayer, FriendlyByteBuf>> globalReceivers = new HashMap<>();
|
|
+
|
|
+ private static void registerGlobalReceiver(ResourceLocation identifier, TriConsumer<MinecraftServer, ServerPlayer, FriendlyByteBuf> consumer) {
|
|
+ globalReceivers.put(identifier, consumer);
|
|
+ }
|
|
+
|
|
+ public static void handlePacket(MinecraftServer server, ServerPlayer player, @NotNull ServerboundCustomPayloadPacket packet) {
|
|
+ globalReceivers.getOrDefault(packet.identifier, ((server1, player1, buf) -> LeavesLogger.LOGGER.severe("NullPotion"))).accept(server, player, packet.data);
|
|
+ }
|
|
+
|
|
+ public static void init() {
|
|
+ entityDataProviders.register(Entity.class, ((data, player, world, entity, showDetails) -> {
|
|
+ UUID ownerUUID = null;
|
|
+ if (entity instanceof OwnableEntity) {
|
|
+ ownerUUID = ((OwnableEntity) entity).getOwnerUUID();
|
|
+ } else if (entity instanceof AbstractHorse) {
|
|
+ ownerUUID = ((AbstractHorse) entity).getOwnerUUID();
|
|
+ }
|
|
+ if (ownerUUID != null) {
|
|
+ MinecraftServer.getServer().getProfileCache().get(ownerUUID).map(GameProfile::getName).ifPresent(name -> data.putString("OwnerName", name));
|
|
+ }
|
|
+ }));
|
|
+ entityDataProviders.register(LivingEntity.class, ((data, player, world, entity, showDetails) -> {
|
|
+ LivingEntity living = (LivingEntity) entity;
|
|
+ Collection<MobEffectInstance> effects = living.getActiveEffects();
|
|
+ if (effects.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ ListTag list = new ListTag();
|
|
+ for (MobEffectInstance effect : effects) {
|
|
+ CompoundTag compound = new CompoundTag();
|
|
+ compound.putString("Name", effect.getDescriptionId());
|
|
+ compound.putInt("Amplifier", effect.getAmplifier());
|
|
+ int duration = Math.min(32767, effect.getDuration());
|
|
+ compound.putInt("Duration", duration);
|
|
+ compound.putBoolean("Bad", effect.getEffect().getCategory() == MobEffectCategory.HARMFUL);
|
|
+ list.add(compound);
|
|
+ }
|
|
+ data.put("Potions", list);
|
|
+ }));
|
|
+ entityDataProviders.register(AgeableMob.class, ((data, player, world, entity, showDetails) -> {
|
|
+ int time = -((AgeableMob) entity).getAge();
|
|
+ if (time > 0) {
|
|
+ data.putInt("GrowingTime", time);
|
|
+ }
|
|
+ }));
|
|
+ entityDataProviders.register(Tadpole.class, ((data, player, world, entity, showDetails) -> {
|
|
+ int time = ((Tadpole) entity).getTicksLeftUntilAdult();
|
|
+ if (time > 0) {
|
|
+ data.putInt("GrowingTime", time);
|
|
+ }
|
|
+ }));
|
|
+ entityDataProviders.register(Animal.class, ((data, player, world, entity, showDetails) -> {
|
|
+ int time = ((Animal) entity).getAge();
|
|
+ if (time > 0) {
|
|
+ data.putInt("BreedingCD", time);
|
|
+ }
|
|
+ }));
|
|
+ entityDataProviders.register(Allay.class, ((data, player, world, entity, showDetails) -> {
|
|
+ int time = 0;
|
|
+ Allay allay = (Allay) entity;
|
|
+ if (allay.duplicationCooldown > 0 && allay.duplicationCooldown < Integer.MAX_VALUE) {
|
|
+ time = (int) allay.duplicationCooldown;
|
|
+ }
|
|
+ if (time > 0) {
|
|
+ data.putInt("BreedingCD", time);
|
|
+ }
|
|
+ }));
|
|
+ entityDataProviders.register(Chicken.class, ((data, player, world, entity, showDetails) -> {
|
|
+ data.putInt("NextEgg", ((Chicken) entity).eggTime / 20);
|
|
+ }));
|
|
+ entityDataProviders.register(Entity.class, ((tag, player, world, object, showDetails) -> {
|
|
+ var groups = getGroups(player, (ServerLevel) world, object, showDetails);
|
|
+ if (groups != null) {
|
|
+ if (ViewGroup.saveList(tag, "JadeItemStorage", groups, item -> {
|
|
+ CompoundTag itemTag = new CompoundTag();
|
|
+ int count = item.getCount();
|
|
+ if (count > 64) item.setCount(1);
|
|
+ item.save(itemTag);
|
|
+ if (count > 64) itemTag.putInt("NewCount", count);
|
|
+ return itemTag;
|
|
+ })) {
|
|
+ tag.putString("JadeItemStorageUid", EntityType.getKey(object.getType()).toString());
|
|
+ } else {
|
|
+ if (object instanceof ContainerEntity containerEntity && containerEntity.getLootTable() != null) {
|
|
+ tag.putBoolean("Loot", true);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }));
|
|
+
|
|
+ tileDataProviders.register(BrewingStandBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ if (object instanceof BrewingStandBlockEntity brewingStand) {
|
|
+ CompoundTag compound = new CompoundTag();
|
|
+ compound.putInt("Time", brewingStand.brewTime);
|
|
+ compound.putInt("Fuel", brewingStand.fuel);
|
|
+ data.put("BrewingStand", compound);
|
|
+ }
|
|
+ }));
|
|
+ tileDataProviders.register(BeehiveBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ data.getAllKeys().clear();
|
|
+ BeehiveBlockEntity beehive = (BeehiveBlockEntity) object;
|
|
+ data.putByte("Bees", (byte) beehive.getOccupantCount());
|
|
+ data.putBoolean("Full", beehive.isFull());
|
|
+ }));
|
|
+ tileDataProviders.register(CommandBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ if (object == null || !player.canUseGameMasterBlocks()) {
|
|
+ return;
|
|
+ }
|
|
+ BaseCommandBlock logic = ((CommandBlockEntity) object).getCommandBlock();
|
|
+ String command = logic.getCommand();
|
|
+ if (command.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ if (command.length() > 40) {
|
|
+ command = command.substring(0, 37) + "...";
|
|
+ }
|
|
+ data.putString("Command", command);
|
|
+ }));
|
|
+ tileDataProviders.register(JukeboxBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ if (object instanceof JukeboxBlockEntity jukebox) {
|
|
+ ItemStack stack = jukebox.getRecord();
|
|
+ if (!stack.isEmpty()) {
|
|
+ data.put("Record", stack.save(new CompoundTag()));
|
|
+ }
|
|
+ }
|
|
+ }));
|
|
+ tileDataProviders.register(LecternBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ ItemStack stack = ((LecternBlockEntity) object).getBook();
|
|
+ if (!stack.isEmpty()) {
|
|
+ if (stack.hasCustomHoverName() || stack.getItem() != Items.WRITABLE_BOOK) {
|
|
+ data.put("Book", stack.save(new CompoundTag()));
|
|
+ }
|
|
+ }
|
|
+ }));
|
|
+ tileDataProviders.register(ComparatorBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ data.putInt("Signal", ((ComparatorBlockEntity) object).getOutputSignal());
|
|
+ }));
|
|
+ tileDataProviders.register(HopperBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ BlockState state = object.getBlockState();
|
|
+ if (state.hasProperty(BlockStateProperties.ENABLED) && !state.getValue(BlockStateProperties.ENABLED)) {
|
|
+ data.putBoolean("HopperLocked", true);
|
|
+ }
|
|
+ }));
|
|
+ tileDataProviders.register(AbstractFurnaceBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ AbstractFurnaceBlockEntity furnace = (AbstractFurnaceBlockEntity) object;
|
|
+ ListTag items = new ListTag();
|
|
+ for (int i = 0; i < 3; i++) {
|
|
+ items.add(furnace.getItem(i).save(new CompoundTag()));
|
|
+ }
|
|
+ data.put("furnace", items);
|
|
+ CompoundTag furnaceTag = furnace.saveWithoutMetadata();
|
|
+ data.putInt("progress", furnaceTag.getInt("CookTime"));
|
|
+ data.putInt("total", furnaceTag.getInt("CookTimeTotal"));
|
|
+ }));
|
|
+ tileDataProviders.register(BlockEntity.class, ((data, player, world, object, showDetails) -> {
|
|
+ if (object instanceof Nameable nameable) {
|
|
+ if (nameable.hasCustomName()) {
|
|
+ data.putString("givenName", Component.Serializer.toJson(nameable.getCustomName()));
|
|
+ }
|
|
+ }
|
|
+ }));
|
|
+ tileDataProviders.register(BlockEntity.class, ((tag, player, world, object, showDetails) -> {
|
|
+ if (object instanceof AbstractFurnaceBlockEntity) {
|
|
+ return;
|
|
+ }
|
|
+ var groups = getGroups(player, (ServerLevel) world, object, showDetails);
|
|
+ if (groups != null) {
|
|
+ if (ViewGroup.saveList(tag, "JadeItemStorage", groups, item -> {
|
|
+ CompoundTag itemTag = new CompoundTag();
|
|
+ int count = item.getCount();
|
|
+ if (count > 64) item.setCount(1);
|
|
+ item.save(itemTag);
|
|
+ if (count > 64) itemTag.putInt("NewCount", count);
|
|
+ return itemTag;
|
|
+ })) {
|
|
+ tag.putString("JadeItemStorageUid", BlockEntityType.getKey(object.getType()).toString());
|
|
+ } else {
|
|
+ if (object instanceof RandomizableContainerBlockEntity te && te.lootTable != null) {
|
|
+ tag.putBoolean("Loot", true);
|
|
+ return;
|
|
+ }
|
|
+ if (!player.isCreative() && !player.isSpectator() && object instanceof BaseContainerBlockEntity te) {
|
|
+ if (te.lockKey != LockCode.NO_LOCK) {
|
|
+ tag.putBoolean("Locked", true);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }));
|
|
+
|
|
+ registerGlobalReceiver(PACKET_REQUEST_TILE, JadeProtocol::requestTileData);
|
|
+ registerGlobalReceiver(PACKET_REQUEST_ENTITY, JadeProtocol::requestEntityData);
|
|
+ }
|
|
+
|
|
+ public static void requestEntityData(MinecraftServer server, ServerPlayer player, FriendlyByteBuf buf) {
|
|
+ if (!LeavesConfig.jadeProtocol) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ Level world = player.level;
|
|
+ Entity entity = world.getEntity(buf.readVarInt());
|
|
+ boolean showDetails = buf.readBoolean();
|
|
+ if (entity == null || player.distanceToSqr(entity) > MAX_DISTANCE_SQR) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ server.execute(() -> {
|
|
+ var providers = entityDataProviders.get(entity);
|
|
+ if (providers.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ CompoundTag tag = new CompoundTag();
|
|
+ for (IJadeProvider<Entity> provider : providers) {
|
|
+ try {
|
|
+ provider.saveData(tag, player, world, entity, showDetails);
|
|
+ } catch (Exception e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+ tag.putInt("WailaEntityID", entity.getId());
|
|
+
|
|
+ FriendlyByteBuf buf1 = new FriendlyByteBuf(Unpooled.buffer());
|
|
+ buf1.writeNbt(tag);
|
|
+ ProtocolUtils.sendPayloadPacket(player, PACKET_RECEIVE_DATA, buf1);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public static void requestTileData(MinecraftServer server, ServerPlayer player, FriendlyByteBuf buf) {
|
|
+ if (!LeavesConfig.jadeProtocol) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ BlockPos pos = buf.readBlockPos();
|
|
+ boolean showDetails = buf.readBoolean();
|
|
+ Level world = player.level;
|
|
+ if (pos.distSqr(player.blockPosition()) > MAX_DISTANCE_SQR || !world.isLoaded(pos)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ server.execute(() -> {
|
|
+ BlockEntity tile = world.getBlockEntity(pos);
|
|
+ if (tile == null) return;
|
|
+
|
|
+ List<IJadeProvider<BlockEntity>> providers = tileDataProviders.get(tile);
|
|
+ if (providers.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ CompoundTag tag = new CompoundTag();
|
|
+ for (IJadeProvider<BlockEntity> provider : providers) {
|
|
+ try {
|
|
+ provider.saveData(tag, player, world, tile, showDetails);
|
|
+ } catch (Exception e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ tag.putInt("x", pos.getX());
|
|
+ tag.putInt("y", pos.getY());
|
|
+ tag.putInt("z", pos.getZ());
|
|
+ tag.putString("id", BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(tile.getType()).toString());
|
|
+
|
|
+ FriendlyByteBuf buf1 = new FriendlyByteBuf(Unpooled.buffer());
|
|
+ buf1.writeNbt(tag);
|
|
+ ProtocolUtils.sendPayloadPacket(player, PACKET_RECEIVE_DATA, buf1);
|
|
+ });
|
|
+
|
|
+ }
|
|
+
|
|
+ public interface IJadeProvider<T> {
|
|
+ public void saveData(CompoundTag data, ServerPlayer player, Level world, T object, boolean showDetails);
|
|
+ }
|
|
+
|
|
+ // Power by Jade
|
|
+
|
|
+ public static List<ViewGroup<ItemStack>> getGroups(ServerPlayer player, ServerLevel world, Object target, boolean showDetails) {
|
|
+ if (target instanceof RandomizableContainerBlockEntity te && te.lootTable != null) {
|
|
+ return List.of();
|
|
+ }
|
|
+ if (target instanceof ContainerEntity containerEntity && containerEntity.getLootTable() != null) {
|
|
+ return List.of();
|
|
+ }
|
|
+ if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
|
|
+ if (te.lockKey != LockCode.NO_LOCK) {
|
|
+ return List.of();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return wrapItemStorage(target, player);
|
|
+ }
|
|
+
|
|
+ public static List<ViewGroup<ItemStack>> wrapItemStorage(Object target, ServerPlayer player) {
|
|
+ int size = 54;
|
|
+ if (target instanceof AbstractHorse horse) {
|
|
+ return List.of(fromContainer(horse.inventory, size, 2));
|
|
+ }
|
|
+ if (target instanceof Container container) {
|
|
+ if (target instanceof ChestBlockEntity be) {
|
|
+ if (be.getBlockState().getBlock() instanceof ChestBlock chestBlock) {
|
|
+ Container compound = ChestBlock.getContainer(chestBlock, be.getBlockState(), be.getLevel(), be.getBlockPos(), false);
|
|
+ if (compound != null) {
|
|
+ container = compound;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return List.of(fromContainer(container, size, 0));
|
|
+ }
|
|
+ if (player != null && target instanceof EnderChestBlockEntity) {
|
|
+ return List.of(fromContainer(player.getEnderChestInventory(), size, 0));
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ public static ViewGroup<ItemStack> fromContainer(Container container, int maxSize, int startIndex) {
|
|
+ return compacted(IntStream.range(startIndex, container.getContainerSize()).limit(maxSize * 3L).mapToObj(container::getItem), maxSize);
|
|
+ }
|
|
+
|
|
+ public static ViewGroup<ItemStack> compacted(Stream<ItemStack> stream, int maxSize) {
|
|
+ List<ItemStack> stacks = Lists.newArrayList();
|
|
+ MutableInt start = new MutableInt();
|
|
+ stream.filter(stack -> !stack.isEmpty()).filter(stack -> {
|
|
+ if (stack.hasTag() && stack.getTag().contains("CustomModelData")) {
|
|
+ for (String key : stack.getTag().getAllKeys()) {
|
|
+ if (key.toLowerCase(Locale.ENGLISH).endsWith("clear") && stack.getTag().getBoolean(key)) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+ }).forEach(stack -> {
|
|
+ int size = stacks.size();
|
|
+ if (size > maxSize) return;
|
|
+ for (int i = 0; i < size; i++) {
|
|
+ int j = (i + start.intValue()) % size;
|
|
+ if (ItemStack.isSameItemSameTags(stack, stacks.get(j))) {
|
|
+ stacks.get(j).grow(stack.getCount());
|
|
+ start.setValue(j);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ stacks.add(stack.copy());
|
|
+ });
|
|
+ if (stacks.size() > maxSize) stacks.remove(maxSize);
|
|
+ return new ViewGroup<>(stacks);
|
|
+ }
|
|
+
|
|
+ public static class ViewGroup<T> {
|
|
+
|
|
+ public final List<T> views;
|
|
+ @Nullable
|
|
+ public String id;
|
|
+ @Nullable
|
|
+ protected CompoundTag extraData;
|
|
+
|
|
+ public ViewGroup(List<T> views) {
|
|
+ this.views = views;
|
|
+ }
|
|
+
|
|
+ public void save(CompoundTag tag, Function<T, CompoundTag> writer) {
|
|
+ ListTag list = new ListTag();
|
|
+ for (var view : views) {
|
|
+ list.add(writer.apply(view));
|
|
+ }
|
|
+ tag.put("Views", list);
|
|
+ if (id != null) {
|
|
+ tag.putString("Id", id);
|
|
+ }
|
|
+ if (extraData != null) {
|
|
+ tag.put("Data", extraData);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static <T> boolean saveList(CompoundTag tag, String key, List<ViewGroup<T>> groups, Function<T, CompoundTag> writer) {
|
|
+ if (groups == null || groups.isEmpty()) return false;
|
|
+ ListTag groupList = new ListTag();
|
|
+ for (ViewGroup<T> group : groups) {
|
|
+ if (group.views.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+ CompoundTag groupTag = new CompoundTag();
|
|
+ group.save(groupTag, writer);
|
|
+ groupList.add(groupTag);
|
|
+ }
|
|
+ if (!groupList.isEmpty()) {
|
|
+ tag.put(key, groupList);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public CompoundTag getExtraData() {
|
|
+ if (extraData == null) {
|
|
+ extraData = new CompoundTag();
|
|
+ }
|
|
+ return extraData;
|
|
+ }
|
|
+
|
|
+ public void setProgress(float progress) {
|
|
+ getExtraData().putFloat("Progress", progress);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class HierarchyLookup<T extends IJadeProvider<?>> {
|
|
+
|
|
+ private final Class<?> baseClass;
|
|
+ private final ListMultimap<Class<?>, T> objects = ArrayListMultimap.create();
|
|
+ private final Cache<Class<?>, List<T>> resultCache = CacheBuilder.newBuilder().build();
|
|
+ private final boolean singleton;
|
|
+
|
|
+ public HierarchyLookup(Class<?> baseClass) {
|
|
+ this(baseClass, false);
|
|
+ }
|
|
+
|
|
+ public HierarchyLookup(Class<?> baseClass, boolean singleton) {
|
|
+ this.baseClass = baseClass;
|
|
+ this.singleton = singleton;
|
|
+ }
|
|
+
|
|
+ public void register(Class<?> clazz, T provider) {
|
|
+ Objects.requireNonNull(clazz);
|
|
+ objects.put(clazz, provider);
|
|
+ }
|
|
+
|
|
+ public List<T> get(Object obj) {
|
|
+ if (obj == null) {
|
|
+ return List.of();
|
|
+ }
|
|
+ return get(obj.getClass());
|
|
+ }
|
|
+
|
|
+ public List<T> get(Class<?> clazz) {
|
|
+ try {
|
|
+ return resultCache.get(clazz, () -> {
|
|
+ List<T> list = Lists.newArrayList();
|
|
+ getInternal(clazz, list);
|
|
+ if (singleton && !list.isEmpty()) return ImmutableList.of(list.get(0));
|
|
+ return list;
|
|
+ });
|
|
+ } catch (ExecutionException e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ 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));
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|