mirror of
https://github.com/LeavesMC/Leaves.git
synced 2026-01-06 15:51:33 +00:00
Update Jade Protocol
This commit is contained in:
@@ -18,12 +18,25 @@ index 4aeab90e778629c355189dfe79c39c4b21f5f5ac..fe8c9b7e7956837829b4fe3eb449b2c0
|
||||
return Math.max(0, Tadpole.ticksToBeFrog - this.age);
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerData.java b/src/main/java/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerData.java
|
||||
index 7292e68199d244990efa8475fb40b94fe72323c0..3c234bfcecd6d93f8ff9b1abc6dfc72c26fa30f0 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerData.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/trialspawner/TrialSpawnerData.java
|
||||
@@ -50,7 +50,7 @@ public class TrialSpawnerData {
|
||||
});
|
||||
protected final Set<UUID> detectedPlayers = new HashSet<>();
|
||||
protected final Set<UUID> currentMobs = new HashSet<>();
|
||||
- protected long cooldownEndsAt;
|
||||
+ public long cooldownEndsAt; // Leaves - protected -> public
|
||||
protected long nextMobSpawnsAt;
|
||||
protected int totalMobsSpawned;
|
||||
protected Optional<SpawnData> nextSpawnData;
|
||||
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..03c4ed0135a8e570723726a402556e37d11308c4
|
||||
index 0000000000000000000000000000000000000000..6ac52cf629d467daf3137304909b8c125b925550
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/top/leavesmc/leaves/protocol/JadeProtocol.java
|
||||
@@ -0,0 +1,622 @@
|
||||
@@ -0,0 +1,901 @@
|
||||
+package top.leavesmc.leaves.protocol;
|
||||
+
|
||||
+import com.google.common.cache.Cache;
|
||||
@@ -32,8 +45,11 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+import com.google.common.collect.ImmutableList;
|
||||
+import com.google.common.collect.ListMultimap;
|
||||
+import com.google.common.collect.Lists;
|
||||
+import com.google.common.collect.Multimap;
|
||||
+import com.mojang.authlib.GameProfile;
|
||||
+import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
|
||||
+import net.minecraft.core.BlockPos;
|
||||
+import net.minecraft.core.Direction;
|
||||
+import net.minecraft.core.registries.BuiltInRegistries;
|
||||
+import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.nbt.ListTag;
|
||||
@@ -44,16 +60,17 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
+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.server.players.GameProfileCache;
|
||||
+import net.minecraft.world.Container;
|
||||
+import net.minecraft.world.LockCode;
|
||||
+import net.minecraft.world.MenuProvider;
|
||||
+import net.minecraft.world.Nameable;
|
||||
+import net.minecraft.world.RandomizableContainer;
|
||||
+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;
|
||||
@@ -61,18 +78,23 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+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.monster.ZombieVillager;
|
||||
+import net.minecraft.world.entity.vehicle.ContainerEntity;
|
||||
+import net.minecraft.world.inventory.PlayerEnderChestContainer;
|
||||
+import net.minecraft.world.item.Item;
|
||||
+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.CalibratedSculkSensorBlock;
|
||||
+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.CalibratedSculkSensorBlockEntity;
|
||||
+import net.minecraft.world.level.block.entity.CampfireBlockEntity;
|
||||
+import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
+import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity;
|
||||
+import net.minecraft.world.level.block.entity.CommandBlockEntity;
|
||||
@@ -81,11 +103,11 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+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.entity.TrialSpawnerBlockEntity;
|
||||
+import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData;
|
||||
+import net.minecraft.world.level.block.state.BlockState;
|
||||
+import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
+import net.minecraft.world.phys.BlockHitResult;
|
||||
+import org.apache.commons.lang3.mutable.MutableInt;
|
||||
+import org.jetbrains.annotations.Contract;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.jetbrains.annotations.Nullable;
|
||||
@@ -100,7 +122,11 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+import java.util.Objects;
|
||||
+import java.util.UUID;
|
||||
+import java.util.concurrent.ExecutionException;
|
||||
+import java.util.concurrent.TimeUnit;
|
||||
+import java.util.concurrent.atomic.AtomicInteger;
|
||||
+import java.util.concurrent.atomic.AtomicLong;
|
||||
+import java.util.function.Function;
|
||||
+import java.util.function.Predicate;
|
||||
+import java.util.stream.IntStream;
|
||||
+import java.util.stream.Stream;
|
||||
+
|
||||
@@ -113,8 +139,13 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ public static final ResourceLocation PACKET_SERVER_PING = id("server_ping");
|
||||
+ public static final ResourceLocation PACKET_RECEIVE_DATA = id("receive_data");
|
||||
+
|
||||
+ private static final HierarchyLookup<IJadeProvider<Entity>> entityDataProviders = new HierarchyLookup<>(Entity.class);
|
||||
+ private static final HierarchyLookup<IJadeProvider<BlockEntity>> tileDataProviders = new HierarchyLookup<>(BlockEntity.class);
|
||||
+ private static final HierarchyLookup<IJadeDataProvider<Entity>> entityDataProviders = new HierarchyLookup<>(Entity.class);
|
||||
+ private static final HierarchyLookup<IJadeDataProvider<BlockEntity>> tileDataProviders = new HierarchyLookup<>(BlockEntity.class);
|
||||
+
|
||||
+ private static final HierarchyLookup<IServerExtensionProvider<Entity, ItemStack>> entityItemProviders = new HierarchyLookup<>(Entity.class);
|
||||
+ private static final HierarchyLookup<IServerExtensionProvider<BlockEntity, ItemStack>> tileItemProviders = new HierarchyLookup<>(BlockEntity.class);
|
||||
+
|
||||
+ public static final Cache<Object, ItemCollector<?>> targetCache = CacheBuilder.newBuilder().weakKeys().expireAfterAccess(60, TimeUnit.SECONDS).build();
|
||||
+
|
||||
+ public static final int MAX_DISTANCE_SQR = 900;
|
||||
+
|
||||
@@ -125,13 +156,101 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+
|
||||
+ @ProtocolHandler.Init
|
||||
+ public static void init() {
|
||||
+ entityDataProviders.register(Entity.class, ((data, player, world, entity, showDetails) -> {
|
||||
+ UUID ownerUUID = null;
|
||||
+ if (entity instanceof OwnableEntity) {
|
||||
+ ownerUUID = ((OwnableEntity) entity).getOwnerUUID();
|
||||
+ entityItemProviders.register(Entity.class, (player, world, target) -> {
|
||||
+ if (target instanceof ContainerEntity containerEntity && containerEntity.getLootTable() != null) {
|
||||
+ return List.of();
|
||||
+ }
|
||||
+
|
||||
+ ItemCollector<?> itemCollector;
|
||||
+ try {
|
||||
+ itemCollector = targetCache.get(target, () -> {
|
||||
+ if (target instanceof AbstractHorse) {
|
||||
+ return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
|
||||
+ if (o instanceof AbstractHorse horse) {
|
||||
+ return horse.inventory;
|
||||
+ }
|
||||
+ return null;
|
||||
+ }, 2));
|
||||
+ }
|
||||
+ return ItemCollector.EMPTY;
|
||||
+ });
|
||||
+ } catch (ExecutionException e) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (itemCollector == ItemCollector.EMPTY) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ return itemCollector.update(target, world.getGameTime());
|
||||
+ });
|
||||
+
|
||||
+ tileItemProviders.register(CampfireBlockEntity.class, (player, world, target) -> {
|
||||
+ CampfireBlockEntity campfire = (CampfireBlockEntity) target;
|
||||
+ 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();
|
||||
+ stack.getOrCreateTag().putInt("jade:cooking", campfire.cookingTime[i] - campfire.cookingProgress[i]);
|
||||
+ list.add(stack);
|
||||
+ }
|
||||
+ return List.of(new ViewGroup<>(list));
|
||||
+ });
|
||||
+ tileItemProviders.register(BlockEntity.class, (player, world, target) -> {
|
||||
+ if (target instanceof RandomizableContainer te && te.getLootTable() != null) {
|
||||
+ return List.of();
|
||||
+ }
|
||||
+
|
||||
+ 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(0)).update(inventory, world.getGameTime());
|
||||
+ }
|
||||
+ ItemCollector<?> itemCollector;
|
||||
+ try {
|
||||
+ itemCollector = targetCache.get(target, () -> {
|
||||
+ if (target instanceof Container) {
|
||||
+ if (target instanceof ChestBlockEntity) {
|
||||
+ return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
|
||||
+ if (o instanceof ChestBlockEntity be) {
|
||||
+ if (be.getBlockState().getBlock() instanceof ChestBlock chestBlock) {
|
||||
+ Container compound = ChestBlock.getContainer(chestBlock, be.getBlockState(), world, be.getBlockPos(), false);
|
||||
+ if (compound != null) {
|
||||
+ return compound;
|
||||
+ }
|
||||
+ }
|
||||
+ return be;
|
||||
+ }
|
||||
+ return null;
|
||||
+ }, 0));
|
||||
+ }
|
||||
+ return new ItemCollector<>(new ItemIterator.ContainerItemIterator(0));
|
||||
+ }
|
||||
+ return ItemCollector.EMPTY;
|
||||
+ });
|
||||
+ } catch (ExecutionException e) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ if (itemCollector == ItemCollector.EMPTY) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ return itemCollector.update(target, world.getGameTime());
|
||||
+ });
|
||||
+
|
||||
+ entityDataProviders.register(OwnableEntity.class, ((data, player, world, entity, showDetails) -> {
|
||||
+ UUID ownerUUID = ((OwnableEntity) entity).getOwnerUUID();
|
||||
+ if (ownerUUID != null) {
|
||||
+ MinecraftServer.getServer().getProfileCache().get(ownerUUID).map(GameProfile::getName).ifPresent(name -> data.putString("OwnerName", name));
|
||||
+ GameProfileCache cache = MinecraftServer.getServer().getProfileCache();
|
||||
+ if (cache != null) {
|
||||
+ cache.get(ownerUUID).map(GameProfile::getName).ifPresent(name -> data.putString("OwnerName", name));
|
||||
+ }
|
||||
+ }
|
||||
+ }));
|
||||
+ entityDataProviders.register(LivingEntity.class, ((data, player, world, entity, showDetails) -> {
|
||||
@@ -183,27 +302,38 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+ }));
|
||||
+ entityDataProviders.register(Chicken.class, ((data, player, world, entity, showDetails) -> {
|
||||
+ data.putInt("NextEgg", ((Chicken) entity).eggTime / 20);
|
||||
+ data.putInt("NextEggIn", ((Chicken) entity).eggTime);
|
||||
+ }));
|
||||
+ entityDataProviders.register(Entity.class, ((tag, player, world, object, showDetails) -> {
|
||||
+ var groups = getGroups(player, (ServerLevel) world, object, showDetails);
|
||||
+ if (groups != null) {
|
||||
+ for (var provider : entityItemProviders.get(object)) {
|
||||
+ var groups = provider.getGroups(player, world, object);
|
||||
+ if (groups == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (ViewGroup.saveList(tag, "JadeItemStorage", groups, item -> {
|
||||
+ CompoundTag itemTag = new CompoundTag();
|
||||
+ int count = item.getCount();
|
||||
+ if (count > 64) item.setCount(1);
|
||||
+ if (count > 64) {
|
||||
+ item.setCount(1);
|
||||
+ }
|
||||
+ item.save(itemTag);
|
||||
+ if (count > 64) itemTag.putInt("NewCount", count);
|
||||
+ if (count > 64) {
|
||||
+ itemTag.putInt("NewCount", count);
|
||||
+ item.setCount(count);
|
||||
+ }
|
||||
+ return itemTag;
|
||||
+ })) {
|
||||
+ tag.putString("JadeItemStorageUid", EntityType.getKey(object.getType()).toString());
|
||||
+ } else {
|
||||
+ if (object instanceof ContainerEntity containerEntity && containerEntity.getLootTable() != null) {
|
||||
+ tag.putBoolean("Loot", true);
|
||||
+ }
|
||||
+ tag.putString("JadeItemStorageUid", "minecraft:item_storage");
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }));
|
||||
+ entityDataProviders.register(ZombieVillager.class, (data, player, world, object, showDetails) -> {
|
||||
+ ZombieVillager entity = (ZombieVillager) object;
|
||||
+ if (entity.villagerConversionTime > 0) {
|
||||
+ data.putInt("ConversionTime", entity.villagerConversionTime);
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ tileDataProviders.register(BrewingStandBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
||||
+ if (object instanceof BrewingStandBlockEntity brewingStand) {
|
||||
@@ -220,7 +350,7 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ data.putBoolean("Full", beehive.isFull());
|
||||
+ }));
|
||||
+ tileDataProviders.register(CommandBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
||||
+ if (object == null || !player.canUseGameMasterBlocks()) {
|
||||
+ if (!player.canUseGameMasterBlocks()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ BaseCommandBlock logic = ((CommandBlockEntity) object).getCommandBlock();
|
||||
@@ -271,8 +401,18 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }));
|
||||
+ 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()));
|
||||
+ Component name = null;
|
||||
+ if (nameable instanceof ChestBlockEntity chestBlock) {
|
||||
+ MenuProvider menuProvider = chestBlock.getBlockState().getMenuProvider(world, chestBlock.getBlockPos());
|
||||
+ if (menuProvider != null) {
|
||||
+ name = menuProvider.getDisplayName();
|
||||
+ }
|
||||
+ } else if (nameable.hasCustomName()) {
|
||||
+ name = nameable.getDisplayName();
|
||||
+ }
|
||||
+
|
||||
+ if (name != null) {
|
||||
+ data.putString("givenName", Component.Serializer.toJson(name));
|
||||
+ }
|
||||
+ }
|
||||
+ }));
|
||||
@@ -286,30 +426,49 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ if (object instanceof AbstractFurnaceBlockEntity) {
|
||||
+ return;
|
||||
+ }
|
||||
+ var groups = getGroups(player, (ServerLevel) world, object, showDetails);
|
||||
+ if (groups != null) {
|
||||
+
|
||||
+ for (var provider : tileItemProviders.get(object)) {
|
||||
+ var groups = provider.getGroups(player, world, object);
|
||||
+ if (groups == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (ViewGroup.saveList(tag, "JadeItemStorage", groups, item -> {
|
||||
+ CompoundTag itemTag = new CompoundTag();
|
||||
+ int count = item.getCount();
|
||||
+ if (count > 64) item.setCount(1);
|
||||
+ if (count > 64) {
|
||||
+ item.setCount(1);
|
||||
+ }
|
||||
+ item.save(itemTag);
|
||||
+ if (count > 64) itemTag.putInt("NewCount", count);
|
||||
+ if (count > 64) {
|
||||
+ itemTag.putInt("NewCount", count);
|
||||
+ item.setCount(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);
|
||||
+ }
|
||||
+ tag.putString("JadeItemStorageUid", "minecraft:item_storage");
|
||||
+ } else if (object instanceof RandomizableContainer containerEntity && containerEntity.getLootTable() != null) {
|
||||
+ tag.putBoolean("Loot", true);
|
||||
+ } else if (!player.isCreative() && !player.isSpectator() && object instanceof BaseContainerBlockEntity te) {
|
||||
+ if (te.lockKey != LockCode.NO_LOCK) {
|
||||
+ tag.putBoolean("Locked", true);
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }));
|
||||
+ tileDataProviders.register(TrialSpawnerBlockEntity.class, (data, player, world, object, showDetails) -> {
|
||||
+ TrialSpawnerBlockEntity spawner = (TrialSpawnerBlockEntity) object;
|
||||
+ TrialSpawnerData spawnerData = spawner.getTrialSpawner().getData();
|
||||
+ if (spawner.getTrialSpawner().canSpawnInLevel(world) && world.getGameTime() < spawnerData.cooldownEndsAt) {
|
||||
+ data.putInt("Cooldown", (int) (spawnerData.cooldownEndsAt - world.getGameTime()));
|
||||
+ }
|
||||
+ });
|
||||
+ tileDataProviders.register(CalibratedSculkSensorBlockEntity.class, ((data, player, world, object, showDetails) -> {
|
||||
+ Direction direction = object.getBlockState().getValue(CalibratedSculkSensorBlock.FACING).getOpposite();
|
||||
+ int signal = world.getSignal(object.getBlockPos().relative(direction), direction);
|
||||
+ data.putInt("Signal", signal);
|
||||
+ }));
|
||||
+ }
|
||||
+
|
||||
+ @ProtocolHandler.PlayerJoin
|
||||
@@ -340,7 +499,7 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+
|
||||
+ CompoundTag tag = new CompoundTag();
|
||||
+ for (IJadeProvider<Entity> provider : providers) {
|
||||
+ for (IJadeDataProvider<Entity> provider : providers) {
|
||||
+ try {
|
||||
+ provider.saveData(tag, player, world, entity, showDetails);
|
||||
+ } catch (Exception e) {
|
||||
@@ -372,13 +531,13 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ BlockEntity tile = world.getBlockEntity(pos);
|
||||
+ if (tile == null) return;
|
||||
+
|
||||
+ List<IJadeProvider<BlockEntity>> providers = tileDataProviders.get(tile);
|
||||
+ List<IJadeDataProvider<BlockEntity>> providers = tileDataProviders.get(tile);
|
||||
+ if (providers.isEmpty()) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ CompoundTag tag = new CompoundTag();
|
||||
+ for (IJadeProvider<BlockEntity> provider : providers) {
|
||||
+ for (IJadeDataProvider<BlockEntity> provider : providers) {
|
||||
+ try {
|
||||
+ provider.saveData(tag, player, world, tile, showDetails);
|
||||
+ } catch (Exception e) {
|
||||
@@ -408,10 +567,6 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public interface IJadeProvider<T> {
|
||||
+ void saveData(CompoundTag data, ServerPlayer player, Level world, T object, boolean showDetails);
|
||||
+ }
|
||||
+
|
||||
+ public record RequestEntityPayload(boolean showDetails, int entityId, float hitX, float hitY, float hitZ) implements CustomPacketPayload {
|
||||
+
|
||||
+ private static final ResourceLocation PACKET_REQUEST_ENTITY = JadeProtocol.id("request_entity");
|
||||
@@ -469,53 +624,26 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ return mutableComponent;
|
||||
+ }
|
||||
+
|
||||
+ 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();
|
||||
+ public interface IJadeProvider {
|
||||
+ }
|
||||
+
|
||||
+ public interface IJadeDataProvider<T> {
|
||||
+ void saveData(CompoundTag data, ServerPlayer player, Level world, T object, boolean showDetails);
|
||||
+ }
|
||||
+
|
||||
+ public interface IServerExtensionProvider<IN, OUT> {
|
||||
+ List<ViewGroup<OUT>> getGroups(ServerPlayer player, Level world, IN target);
|
||||
+ }
|
||||
+
|
||||
+ public static class ItemCollector<T> {
|
||||
+ public static final int MAX_SIZE = 54;
|
||||
+ public static final ItemCollector<?> EMPTY = new ItemCollector<>(null);
|
||||
+ private static final Predicate<ItemStack> NON_EMPTY = stack -> {
|
||||
+ if (stack.isEmpty()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ 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")) {
|
||||
+ CompoundTag tag = stack.getTag();
|
||||
+ if (tag != null && tag.contains("CustomModelData")) {
|
||||
+ for (String key : stack.getTag().getAllKeys()) {
|
||||
+ if (key.toLowerCase(Locale.ENGLISH).endsWith("clear") && stack.getTag().getBoolean(key)) {
|
||||
+ return false;
|
||||
@@ -523,21 +651,180 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+ }
|
||||
+ 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;
|
||||
+ }
|
||||
+ };
|
||||
+ private final Object2IntLinkedOpenHashMap<ItemDefinition> items = new Object2IntLinkedOpenHashMap<>();
|
||||
+ private final ItemIterator<T> iterator;
|
||||
+ public long version;
|
||||
+ public long lastTimeFinished;
|
||||
+ public List<ViewGroup<ItemStack>> mergedResult;
|
||||
+
|
||||
+ public ItemCollector(ItemIterator<T> iterator) {
|
||||
+ this.iterator = iterator;
|
||||
+ }
|
||||
+
|
||||
+ public List<ViewGroup<ItemStack>> update(Object target, long gameTime) {
|
||||
+ if (iterator == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ stacks.add(stack.copy());
|
||||
+ });
|
||||
+ if (stacks.size() > maxSize) stacks.remove(maxSize);
|
||||
+ return new ViewGroup<>(stacks);
|
||||
+ T container = iterator.find(target);
|
||||
+ if (container == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ long currentVersion = iterator.getVersion(container);
|
||||
+ if (mergedResult != null && iterator.isFinished()) {
|
||||
+ if (version == currentVersion) {
|
||||
+ return mergedResult; // content not changed
|
||||
+ }
|
||||
+ if (lastTimeFinished + 5 > gameTime) {
|
||||
+ return mergedResult; // avoid update too frequently
|
||||
+ }
|
||||
+ iterator.reset();
|
||||
+ }
|
||||
+ AtomicInteger count = new AtomicInteger();
|
||||
+ iterator.populate(container).forEach(stack -> {
|
||||
+ count.incrementAndGet();
|
||||
+ if (NON_EMPTY.test(stack)) {
|
||||
+ ItemDefinition def = new ItemDefinition(stack);
|
||||
+ items.addTo(def, stack.getCount());
|
||||
+ }
|
||||
+ });
|
||||
+ iterator.afterPopulate(count.get());
|
||||
+ if (mergedResult != null && !iterator.isFinished()) {
|
||||
+ updateCollectingProgress(mergedResult.get(0));
|
||||
+ return mergedResult;
|
||||
+ }
|
||||
+ List<ItemStack> partialResult = items.object2IntEntrySet().stream().limit(54).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;
|
||||
+ version = currentVersion;
|
||||
+ lastTimeFinished = gameTime;
|
||||
+ items.clear();
|
||||
+ }
|
||||
+ return groups;
|
||||
+ }
|
||||
+
|
||||
+ protected ViewGroup<ItemStack> updateCollectingProgress(ViewGroup<ItemStack> group) {
|
||||
+ float progress = iterator.getCollectingProgress();
|
||||
+ CompoundTag data = group.getExtraData();
|
||||
+ if (Float.isNaN(progress)) {
|
||||
+ progress = 0;
|
||||
+ }
|
||||
+ if (progress >= 1) {
|
||||
+ data.remove("Collecting");
|
||||
+ } else {
|
||||
+ data.putFloat("Collecting", progress);
|
||||
+ }
|
||||
+ return group;
|
||||
+ }
|
||||
+
|
||||
+ public record ItemDefinition(Item item, @Nullable CompoundTag tag) {
|
||||
+ ItemDefinition(ItemStack stack) {
|
||||
+ this(stack.getItem(), stack.getTag());
|
||||
+ }
|
||||
+
|
||||
+ public ItemStack toStack(int count) {
|
||||
+ ItemStack stack = new ItemStack(item);
|
||||
+ stack.setCount(count);
|
||||
+ stack.setTag(tag);
|
||||
+ return stack;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static abstract class ItemIterator<T> {
|
||||
+ public static final AtomicLong version = new AtomicLong();
|
||||
+ protected final Function<Object, @Nullable T> containerFinder;
|
||||
+ protected final int fromIndex;
|
||||
+ protected boolean finished;
|
||||
+ protected int currentIndex;
|
||||
+
|
||||
+ protected ItemIterator(Function<Object, @Nullable T> containerFinder, int fromIndex) {
|
||||
+ this.containerFinder = containerFinder;
|
||||
+ this.currentIndex = this.fromIndex = fromIndex;
|
||||
+ }
|
||||
+
|
||||
+ public @Nullable T find(Object target) {
|
||||
+ return containerFinder.apply(target);
|
||||
+ }
|
||||
+
|
||||
+ public final boolean isFinished() {
|
||||
+ return finished;
|
||||
+ }
|
||||
+
|
||||
+ public long getVersion(T container) {
|
||||
+ return version.getAndIncrement();
|
||||
+ }
|
||||
+
|
||||
+ public abstract Stream<ItemStack> populate(T container);
|
||||
+
|
||||
+ public void reset() {
|
||||
+ currentIndex = fromIndex;
|
||||
+ finished = false;
|
||||
+ }
|
||||
+
|
||||
+ public void afterPopulate(int count) {
|
||||
+ currentIndex += count;
|
||||
+ if (count == 0 || currentIndex >= 10000) {
|
||||
+ finished = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public float getCollectingProgress() {
|
||||
+ return Float.NaN;
|
||||
+ }
|
||||
+
|
||||
+ public static abstract class SlottedItemIterator<T> extends ItemIterator<T> {
|
||||
+ protected float progress;
|
||||
+
|
||||
+ public SlottedItemIterator(Function<Object, @Nullable T> containerFinder, int fromIndex) {
|
||||
+ super(containerFinder, fromIndex);
|
||||
+ }
|
||||
+
|
||||
+ protected abstract int getSlotCount(T container);
|
||||
+
|
||||
+ protected abstract ItemStack getItemInSlot(T container, int slot);
|
||||
+
|
||||
+ @Override
|
||||
+ public Stream<ItemStack> populate(T container) {
|
||||
+ int slotCount = getSlotCount(container);
|
||||
+ int toIndex = currentIndex + ItemCollector.MAX_SIZE * 2;
|
||||
+ if (toIndex >= slotCount) {
|
||||
+ toIndex = slotCount;
|
||||
+ finished = true;
|
||||
+ }
|
||||
+ progress = (float) (currentIndex - fromIndex) / (slotCount - fromIndex);
|
||||
+ return IntStream.range(currentIndex, toIndex).mapToObj(slot -> getItemInSlot(container, slot));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public float getCollectingProgress() {
|
||||
+ return progress;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static class ContainerItemIterator extends SlottedItemIterator<Container> {
|
||||
+ public ContainerItemIterator(int fromIndex) {
|
||||
+ this(Container.class::cast, fromIndex);
|
||||
+ }
|
||||
+
|
||||
+ public ContainerItemIterator(Function<Object, @Nullable Container> containerFinder, int fromIndex) {
|
||||
+ super(containerFinder, fromIndex);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected int getSlotCount(Container container) {
|
||||
+ return container.getContainerSize();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected ItemStack getItemInSlot(Container container, int slot) {
|
||||
+ return container.getItem(slot);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static class ViewGroup<T> {
|
||||
@@ -567,7 +854,10 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+
|
||||
+ public static <T> boolean saveList(CompoundTag tag, String key, List<ViewGroup<T>> groups, Function<T, CompoundTag> writer) {
|
||||
+ if (groups == null || groups.isEmpty()) return false;
|
||||
+ if (groups == null || groups.isEmpty()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ ListTag groupList = new ListTag();
|
||||
+ for (ViewGroup<T> group : groups) {
|
||||
+ if (group.views.isEmpty()) {
|
||||
@@ -590,18 +880,14 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+ return extraData;
|
||||
+ }
|
||||
+
|
||||
+ public void setProgress(float progress) {
|
||||
+ getExtraData().putFloat("Progress", progress);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static class HierarchyLookup<T extends IJadeProvider<?>> {
|
||||
+ public static class HierarchyLookup<T> {
|
||||
+
|
||||
+ 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;
|
||||
+ private ListMultimap<Class<?>, T> objects = ArrayListMultimap.create();
|
||||
+
|
||||
+ public HierarchyLookup(Class<?> baseClass) {
|
||||
+ this(baseClass, false);
|
||||
@@ -629,7 +915,9 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ return resultCache.get(clazz, () -> {
|
||||
+ List<T> list = Lists.newArrayList();
|
||||
+ getInternal(clazz, list);
|
||||
+ if (singleton && !list.isEmpty()) return ImmutableList.of(list.get(0));
|
||||
+ if (singleton && !list.isEmpty()) {
|
||||
+ return ImmutableList.of(list.get(0));
|
||||
+ }
|
||||
+ return list;
|
||||
+ });
|
||||
+ } catch (ExecutionException e) {
|
||||
@@ -644,5 +932,9 @@ index 0000000000000000000000000000000000000000..03c4ed0135a8e570723726a402556e37
|
||||
+ }
|
||||
+ list.addAll(objects.get(clazz));
|
||||
+ }
|
||||
+
|
||||
+ public Multimap<Class<?>, T> getObjects() {
|
||||
+ return objects;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
|
||||
Reference in New Issue
Block a user