aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/anthonyhilyard/iceberg/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/anthonyhilyard/iceberg/util')
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/EntityCollector.java272
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java6
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java48
3 files changed, 326 insertions, 0 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/EntityCollector.java b/src/main/java/com/anthonyhilyard/iceberg/util/EntityCollector.java
new file mode 100644
index 0000000..1524a88
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/EntityCollector.java
@@ -0,0 +1,272 @@
+package com.anthonyhilyard.iceberg.util;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.authlib.GameProfile;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.core.Holder;
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.sounds.SoundEvent;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.util.AbortableIterationConsumer;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.flag.FeatureFlagSet;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.SpawnEggItem;
+import net.minecraft.world.item.context.UseOnContext;
+import net.minecraft.world.item.crafting.RecipeManager;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.chunk.ChunkSource;
+import net.minecraft.world.level.entity.EntityTypeTest;
+import net.minecraft.world.level.entity.LevelEntityGetter;
+import net.minecraft.world.level.gameevent.GameEvent;
+import net.minecraft.world.level.gameevent.GameEvent.Context;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
+import net.minecraft.world.phys.AABB;
+import net.minecraft.world.phys.BlockHitResult;
+import net.minecraft.world.phys.Vec3;
+import net.minecraft.world.scores.Scoreboard;
+import net.minecraft.world.ticks.LevelTickAccess;
+
+public class EntityCollector extends Level
+{
+ private final Level wrappedLevel;
+ private final List<Entity> collectedEntities = Lists.newArrayList();
+ private BlockState blockState = Blocks.AIR.defaultBlockState();
+
+ private static final Map<Level, EntityCollector> wrappedLevelsMap = Maps.newHashMap();
+ private static final Map<ItemClassPair, Boolean> itemCreatesEntityResultCache = Maps.newHashMap();
+
+ private record ItemClassPair(Item item, Class<?> targetClass) {}
+
+ protected EntityCollector(Level wrapped)
+ {
+ super(null, null, wrapped.dimensionTypeRegistration(), wrapped.getProfilerSupplier(), false, wrapped.isDebug(), 0, 0);
+ wrappedLevel = wrapped;
+ }
+
+ public static EntityCollector of(Level wrappedLevel)
+ {
+ if (!wrappedLevelsMap.containsKey(wrappedLevel))
+ {
+ wrappedLevelsMap.put(wrappedLevel, new EntityCollector(wrappedLevel));
+ }
+
+ return wrappedLevelsMap.get(wrappedLevel);
+ }
+
+ public static List<Entity> collectEntitiesFromItem(Item item)
+ {
+ Minecraft minecraft = Minecraft.getInstance();
+ List<Entity> entities = Lists.newArrayList();
+
+ try
+ {
+ Player dummyPlayer = new Player(minecraft.player.level, BlockPos.ZERO, 0.0f, new GameProfile(null, "_dummy")) {
+ @Override public boolean isSpectator() { return false; }
+ @Override public boolean isCreative() { return false; }
+ };
+
+ dummyPlayer.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(item));
+
+ EntityCollector levelWrapper = EntityCollector.of(dummyPlayer.level);
+
+ if (item instanceof SpawnEggItem spawnEggItem)
+ {
+ entities.add(spawnEggItem.getType(new CompoundTag()).create(levelWrapper));
+ }
+ else
+ {
+ item.use(levelWrapper, dummyPlayer, InteractionHand.MAIN_HAND);
+ }
+
+ entities.addAll(levelWrapper.getCollectedEntities());
+
+ // If we didn't spawn any entities, try again but this time on a simulated rail for minecart-like items.
+ if (entities.isEmpty())
+ {
+ levelWrapper.setBlockState(Blocks.RAIL.defaultBlockState());
+ item.useOn(new UseOnContext(levelWrapper, dummyPlayer, InteractionHand.MAIN_HAND, dummyPlayer.getItemInHand(InteractionHand.MAIN_HAND), new BlockHitResult(Vec3.ZERO, Direction.DOWN, BlockPos.ZERO, false)));
+ levelWrapper.setBlockState(Blocks.AIR.defaultBlockState());
+
+ entities.addAll(levelWrapper.getCollectedEntities());
+ }
+ }
+ catch (Exception e)
+ {
+ // Ignore any errors.
+ }
+
+ return entities;
+ }
+
+ public static <T extends Entity> boolean itemCreatesEntity(Item item, Class<T> targetClass)
+ {
+ ItemClassPair key = new ItemClassPair(item, targetClass);
+ boolean result = false;
+ if (!itemCreatesEntityResultCache.containsKey(key))
+ {
+ // Return true if any collected entities from this item are a subclass of the given type.
+ for (Entity entity : collectEntitiesFromItem(item))
+ {
+ if (targetClass.isInstance(entity))
+ {
+ result = true;
+ break;
+ }
+ }
+
+ itemCreatesEntityResultCache.put(key, result);
+ }
+
+ return itemCreatesEntityResultCache.get(key);
+ }
+
+ public List<Entity> getCollectedEntities()
+ {
+ // Clear the collected entities after this method is called.
+ List<Entity> entities = Lists.newArrayList();
+ entities.addAll(collectedEntities);
+ collectedEntities.clear();
+ return entities;
+ }
+
+ public void setBlockState(BlockState blockState)
+ {
+ this.blockState = blockState;
+ }
+
+ @Override
+ public BlockState getBlockState(BlockPos blockPos)
+ {
+ return blockState;
+ }
+
+ @Override
+ public boolean noCollision(Entity entity, AABB boundingBox)
+ {
+ return true;
+ }
+
+ @Override
+ public boolean addFreshEntity(Entity entity)
+ {
+ collectedEntities.add(entity);
+ return false;
+ }
+
+ @Override
+ public LevelTickAccess<Block> getBlockTicks() { return wrappedLevel.getBlockTicks(); }
+
+ @Override
+ public LevelTickAccess<Fluid> getFluidTicks() { return wrappedLevel.getFluidTicks(); }
+
+ @Override
+ public ChunkSource getChunkSource() { return wrappedLevel.getChunkSource(); }
+
+
+ @Override
+ public void levelEvent(Player p_46771_, int p_46772_, BlockPos p_46773_, int p_46774_) { /* No events. */ }
+
+ @Override
+ public void gameEvent(GameEvent p_220404_, Vec3 p_220405_, Context p_220406_) { /* No events. */ }
+
+
+ @Override
+ public List<? extends Player> players() { return wrappedLevel.players(); }
+
+
+ @Override
+ public Holder<Biome> getUncachedNoiseBiome(int p_204159_, int p_204160_, int p_204161_) { return wrappedLevel.getUncachedNoiseBiome(p_204159_, p_204160_, p_204161_); }
+
+
+ @Override
+ public RegistryAccess registryAccess() { return wrappedLevel.registryAccess(); }
+
+
+ @Override
+ public FeatureFlagSet enabledFeatures() { return wrappedLevel.enabledFeatures(); }
+
+ @Override
+ public float getShade(Direction p_45522_, boolean p_45523_) { return wrappedLevel.getShade(p_45522_, p_45523_); }
+
+
+ @Override
+ public void sendBlockUpdated(BlockPos p_46612_, BlockState p_46613_, BlockState p_46614_, int p_46615_) { /* No block updates. */ }
+
+ @Override
+ public void playSeededSound(Player p_262953_, double p_263004_, double p_263398_, double p_263376_, Holder<SoundEvent> p_263359_, SoundSource p_263020_, float p_263055_, float p_262914_, long p_262991_) { /* No sounds. */ }
+
+ @Override
+ public void playSeededSound(Player p_220372_, Entity p_220373_, Holder<SoundEvent> p_263500_, SoundSource p_220375_, float p_220376_, float p_220377_, long p_220378_) { /* No sounds. */ }
+
+
+ @Override
+ public String gatherChunkSourceStats() { return wrappedLevel.gatherChunkSourceStats(); }
+
+ @Override
+ public Entity getEntity(int p_46492_) { return null; }
+
+
+ @Override
+ public MapItemSavedData getMapData(String p_46650_) { return wrappedLevel.getMapData(p_46650_); }
+
+ @Override
+ public void setMapData(String p_151533_, MapItemSavedData p_151534_) { /* No map data updates. */ }
+
+ @Override
+ public int getFreeMapId() { return wrappedLevel.getFreeMapId(); }
+
+ @Override
+ public void destroyBlockProgress(int p_46506_, BlockPos p_46507_, int p_46508_) { /* No block updates. */ }
+
+
+ @Override
+ public Scoreboard getScoreboard() { return wrappedLevel.getScoreboard(); }
+
+ @Override
+ public RecipeManager getRecipeManager() { return wrappedLevel.getRecipeManager(); }
+
+ @Override
+ public LevelEntityGetter<Entity> getEntities()
+ {
+ return new LevelEntityGetter<Entity>() {
+
+ @Override
+ public Entity get(int p_156931_) { return null; }
+
+ @Override
+ public Entity get(UUID p_156939_) { return null; }
+
+ @Override
+ public Iterable<Entity> getAll() { return List.of(); }
+
+ @Override
+ public <U extends Entity> void get(EntityTypeTest<Entity, U> p_156935_, AbortableIterationConsumer<U> p_261602_) {}
+
+ @Override
+ public void get(AABB p_156937_, Consumer<Entity> p_156938_) {}
+
+ @Override
+ public <U extends Entity> void get(EntityTypeTest<Entity, U> p_156932_, AABB p_156933_, AbortableIterationConsumer<U> p_261542_) {}
+ };
+
+ }
+}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java
index df9e292..0e1a3e1 100644
--- a/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/Selectors.java
@@ -127,6 +127,12 @@ public class Selectors
return validateSelector(value.substring(1));
}
+ // If this is a wildcard selector, it is valid.
+ if (value.contentEquals("*"))
+ {
+ return true;
+ }
+
// This is a tag, which should be a resource location.
if (value.startsWith("$"))
{
diff --git a/src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java b/src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java
new file mode 100644
index 0000000..b49e29d
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/UnsafeUtil.java
@@ -0,0 +1,48 @@
+package com.anthonyhilyard.iceberg.util;
+
+
+import sun.misc.Unsafe;
+
+
+import java.lang.reflect.Field;
+
+
+public class UnsafeUtil
+{
+ private static final Unsafe UNSAFE;
+
+
+ static
+ {
+ try
+ {
+ Field field = Unsafe.class.getDeclaredField("theUnsafe");
+ field.setAccessible(true);
+
+
+ UNSAFE = (Unsafe) field.get(null);
+ }
+ catch (NoSuchFieldException | IllegalAccessException e)
+ {
+ throw new RuntimeException("Couldn't obtain reference to sun.misc.Unsafe", e);
+ }
+ }
+
+
+ public static float readFloat(long address)
+ {
+ return UNSAFE.getFloat(address);
+ }
+
+
+ public static int readInt(long address)
+ {
+ return UNSAFE.getInt(address);
+ }
+
+
+ public static byte readByte(long address)
+ {
+ return UNSAFE.getByte(address);
+ }
+} \ No newline at end of file