2021-04-18 02:13:30 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: JellySquid <jellysquid+atwork@protonmail.com>
Date: Sun, 24 Jan 2021 12:17:19 +0100
Subject: [PATCH] lithium AI
Co-authored-by: Hugo Planque <hookwood01@gmail.com>
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeCache.java b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..b97dc93ba3708811cd8a1409aa5992a548586ed0
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeCache.java
@@ -0,0 +1,92 @@
+package me.jellysquid.mods.lithium.common.ai.pathing;
+
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
+import net.minecraft.world.level.chunk.ChunkSection;
+import net.minecraft.world.level.block.state.IBlockData;
+import net.minecraft.world.level.pathfinder.PathType;
+
+public class PathNodeCache {
+ /**
+ * A transient hash table of chunk sections and whether or not they contain dangerous block types. Used as a cache
+ * to avoid scanning for many neighbors when we know the chunk is free of dangers. This is only safe to use when
+ * we know the world is not going to be modified while it is active.
+ */
+ private static final Reference2BooleanMap<ChunkSection> chunkNeighborDangerCache = new Reference2BooleanOpenHashMap<>();
+
+ /**
+ * True if the chunk danger cache is enabled and can be used.
+ */
+ private static boolean dangerCacheEnabled = false;
+
+ /**
+ * The previous chunk section that was queried for neighboring dangers.
+ */
+ private static ChunkSection prevQueriedNeighborSectionKey;
+
+ /**
+ * The result of the previous query belonging to {@link PathNodeCache#prevQueriedNeighborSectionKey}.
+ */
+ private static boolean prevQueriedNeighborSectionResult;
+
+ /**
+ * Enables the chunk danger cache. This should be called immediately before a controlled path-finding code path
+ * begins so that we can accelerate nearby danger checks.
+ */
+ public static void enableChunkCache() {
+ dangerCacheEnabled = true;
+ }
+
+ /**
+ * Disables and clears the chunk danger cache. This should be called immediately before path-finding ends so that
+ * block updates are reflected for future path-finding tasks.
+ */
+ public static void disableChunkCache() {
+ dangerCacheEnabled = false;
+ chunkNeighborDangerCache.clear();
+
+ prevQueriedNeighborSectionKey = null;
+ prevQueriedNeighborSectionResult = false;
+ }
+
+ private static boolean isChunkSectionDangerousNeighbor(ChunkSection section) {
+ return section.getBlocks()
+ .contains(state -> getNeighborPathNodeType(state) != PathType.OPEN);
+ }
+
+ public static PathType getPathNodeType(IBlockData state) {
+ return state.getPathNodeType();
+ }
+
+ public static PathType getNeighborPathNodeType(IBlockData state) {
+ return state.getNeighborPathNodeType();
+ }
+
+ /**
+ * Returns whether or not a chunk section is free of dangers. This makes use of a caching layer to greatly
+ * accelerate neighbor danger checks when path-finding.
+ *
+ * @param section The chunk section to test for dangers
+ * @return True if this neighboring section is free of any dangers, otherwise false if it could
+ * potentially contain dangers
+ */
+ public static boolean isSectionSafeAsNeighbor(ChunkSection section) {
+ // Empty sections can never contribute a danger
+ if (ChunkSection.isEmpty(section)) {
+ return true;
+ }
+
+ // If the caching code path is disabled, the section must be assumed to potentially contain dangers
+ if (!dangerCacheEnabled) {
+ return false;
+ }
+
+ if (prevQueriedNeighborSectionKey != section) {
+ prevQueriedNeighborSectionKey = section;
+ prevQueriedNeighborSectionResult = !chunkNeighborDangerCache.computeBooleanIfAbsent(section,
+ PathNodeCache::isChunkSectionDangerousNeighbor);
+ }
+
+ return prevQueriedNeighborSectionResult;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults.java b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults.java
new file mode 100644
index 0000000000000000000000000000000000000000..4427f2171671896c978908b1c3d72b3f64f16a0b
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults.java
@@ -0,0 +1,110 @@
+package me.jellysquid.mods.lithium.common.ai.pathing;
+
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.BlockCampfire;
+import net.minecraft.world.level.block.BlockDoor;
+import net.minecraft.world.level.block.BlockFenceGate;
+import net.minecraft.world.level.block.BlockLeaves;
+import net.minecraft.world.level.block.BlockMinecartTrackAbstract;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.block.state.IBlockData;
+import net.minecraft.world.level.material.Material;
+import net.minecraft.world.level.pathfinder.PathType;
+import net.minecraft.tags.TagsBlock;
+import net.minecraft.tags.TagsFluid;
+
+public class PathNodeDefaults {
+ public static PathType getNeighborNodeType(IBlockData state) {
+ if (state.isAir()) {
+ return PathType.OPEN;
+ }
+
+ // [VanillaCopy] LandPathNodeMaker#getNodeTypeFromNeighbors
+ // Determine what kind of obstacle type this neighbor is
+ if (state.a(Blocks.CACTUS)) {
+ return PathType.DANGER_CACTUS;
+ } else if (state.a(Blocks.SWEET_BERRY_BUSH)) {
+ return PathType.DANGER_OTHER;
+ } else if (isFireDangerSource(state)) {
+ return PathType.DANGER_FIRE;
+ } else if (state.getFluid().a(TagsFluid.WATER)) {
+ return PathType.WATER_BORDER;
+ } else {
+ return PathType.OPEN;
+ }
+ }
+
+ public static PathType getNodeType(IBlockData state) {
+ if (state.isAir()) {
+ return PathType.OPEN;
+ }
+
+ Block block = state.getBlock();
+ Material material = state.getMaterial();
+
+ if (state.hasTag(TagsBlock.TRAPDOORS) || state.a(Blocks.LILY_PAD)) {
+ return PathType.TRAPDOOR;
+ }
+
+ if (state.a(Blocks.CACTUS)) {
+ return PathType.DAMAGE_CACTUS;
+ }
+
+ if (state.a(Blocks.SWEET_BERRY_BUSH) || state.a(Blocks.STONECUTTER)) {
+ return PathType.DAMAGE_OTHER;
+ }
+
+ if (state.a(Blocks.HONEY_BLOCK)) {
+ return PathType.STICKY_HONEY;
+ }
+
+ if (state.a(Blocks.COCOA)) {
+ return PathType.COCOA;
+ }
+
+ if (isFireDangerSource(state)) {
+ return PathType.DAMAGE_FIRE;
+ }
+
+ if (BlockDoor.l(state) && !state.get(BlockDoor.OPEN)) {
+ return PathType.DOOR_WOOD_CLOSED;
+ }
+
+ if ((block instanceof BlockDoor) && (material == Material.ORE) && !state.get(BlockDoor.OPEN)) {
+ return PathType.DOOR_IRON_CLOSED;
+ }
+
+ if ((block instanceof BlockDoor) && state.get(BlockDoor.OPEN)) {
+ return PathType.DOOR_OPEN;
+ }
+
+ if (block instanceof BlockMinecartTrackAbstract) {
+ return PathType.RAIL;
+ }
+
+ if (block instanceof BlockLeaves) {
+ return PathType.LEAVES;
+ }
+
+ if (block.a(TagsBlock.FENCES) || block.a(TagsBlock.WALLS) || ((block instanceof BlockFenceGate) && !state.get(BlockFenceGate.OPEN))) {
+ return PathType.FENCE;
+ }
+
+ // Retrieve the fluid state from the block state to avoid a second lookup
+ Fluid fluid = state.getFluid();
+ if (fluid == null) {
+ return PathType.OPEN;
+ } else if (fluid.a(TagsFluid.WATER)) {
+ return PathType.WATER;
+ } else if (fluid.a(TagsFluid.LAVA)) {
+ return PathType.LAVA;
+ }
+
+ return PathType.OPEN;
+ }
+
+ private static boolean isFireDangerSource(IBlockData blockState) {
+ return blockState.a(TagsBlock.FIRE) || blockState.a(Blocks.LAVA) || blockState.a(Blocks.MAGMA_BLOCK) || BlockCampfire.g(blockState);
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java
new file mode 100644
index 0000000000000000000000000000000000000000..23ea99d0ec8622eadadc2073022e59c4aac8dc3a
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java
@@ -0,0 +1,254 @@
+package me.jellysquid.mods.lithium.common.entity.tracker;
+
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListener;
+import net.minecraft.core.BlockPosition;
+import net.minecraft.world.entity.EntityLiving;
+import net.minecraft.core.SectionPosition;
+import net.minecraft.world.level.levelgen.structure.StructureBoundingBox;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tracks the entities within a world and provides notifications to listeners when a tracked entity enters or leaves a
+ * watched area. This removes the necessity to constantly poll the world for nearby entities each tick and generally
+ * provides a sizable boost to performance.
+ */
+public class EntityTrackerEngine {
+ private final Long2ObjectOpenHashMap<TrackedEntityList> sections;
+ private final Reference2ReferenceOpenHashMap<NearbyEntityListener, List<TrackedEntityList>> sectionsByEntity;
+
+
+ public EntityTrackerEngine() {
+ this.sections = new Long2ObjectOpenHashMap<>();
+ this.sectionsByEntity = new Reference2ReferenceOpenHashMap<>();
+ }
+
+ /**
+ * Called when an entity is added to the world.
+ */
+ public void onEntityAdded(int x, int y, int z, EntityLiving entity) {
+ if (this.addEntity(x, y, z, entity)) {
+ this.addListener(x, y, z, entity.getListener());
+ }
+ }
+
+ /**
+ * Called when an entity is removed from the world.
+ */
+ public void onEntityRemoved(int x, int y, int z, EntityLiving entity) {
+ if (this.removeEntity(x, y, z, entity)) {
+ this.removeListener(entity.getListener());
+ }
+ }
+
+ private boolean addEntity(int x, int y, int z, EntityLiving entity) {
+ return this.getOrCreateList(x, y, z).addTrackedEntity(entity);
+ }
+
+ private boolean removeEntity(int x, int y, int z, EntityLiving entity) {
+ TrackedEntityList list = this.getList(x, y, z);
+
+ if (list == null) {
+ return false;
+ }
+
+ return list.removeTrackedEntity(entity);
+ }
+
+ private void addListener(int x, int y, int z, NearbyEntityListener listener) {
+ int r = listener.getChunkRange();
+
+ if (r == 0) {
+ return;
+ }
+
+ if (this.sectionsByEntity.containsKey(listener)) {
+
+ throw new IllegalStateException(errorMessageAlreadyListening(this.sectionsByEntity, listener, SectionPosition.a(x, y, z)));
+ }
+
+ int yMin = Math.max(0, y - r);
+ int yMax = Math.min(y + r, 15);
+
+ List<TrackedEntityList> all = new ArrayList<>((2 * r + 1) * (yMax - yMin + 1) * (2 * r + 1));
+
+ for (int x2 = x - r; x2 <= x + r; x2++) {
+ for (int y2 = yMin; y2 <= yMax; y2++) {
+ for (int z2 = z - r; z2 <= z + r; z2++) {
+ TrackedEntityList list = this.getOrCreateList(x2, y2, z2);
+ list.addListener(listener);
+
+ all.add(list);
+ }
+ }
+ }
+
+ this.sectionsByEntity.put(listener, all);
+ }
+
+ private void removeListener(NearbyEntityListener listener) {
+ int r = listener.getChunkRange();
+
+ if (r == 0) {
+ return;
+ }
+
+ List<TrackedEntityList> all = this.sectionsByEntity.remove(listener);
+
+ if (all != null) {
+ for (TrackedEntityList list : all) {
+ list.removeListener(listener);
+ }
+ } else {
+ throw new IllegalArgumentException("Entity listener not tracked:" + listener.toString());
+ }
+ }
+
+ // Faster implementation which avoids removing from/adding to every list twice on an entity move event
+ private void moveListener(int aX, int aY, int aZ, int bX, int bY, int bZ, NearbyEntityListener listener) {
+ int radius = listener.getChunkRange();
+
+ if (radius == 0) {
+ return;
+ }
+
+ StructureBoundingBox before = new StructureBoundingBox(aX - radius, aY - radius, aZ - radius, aX + radius, aY + radius, aZ + radius);
+ StructureBoundingBox after = new StructureBoundingBox(aX - radius, aY - radius, aZ - radius, bX + radius, bY + radius, bZ + radius);
+
+ StructureBoundingBox merged = new StructureBoundingBox(before);
+ merged.c(after);
+
+ BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition();
+
+ for (int x = merged.a; x <= merged.d; x++) {
+ for (int y = merged.b; y <= merged.e; y++) {
+ for (int z = merged.c; z <= merged.f; z++) {
+ pos.setValues(x, y, z);
+
+ boolean leaving = before.hasPoint(pos);
+ boolean entering = after.hasPoint(pos);
+
+ // Nothing to change
+ if (leaving == entering) {
+ continue;
+ }
+
+ if (leaving) {
+ // The listener has left the chunk
+ TrackedEntityList list = this.getList(x, y, z);
+
+ if (list == null) {
+ throw new IllegalStateException("Expected there to be a listener list while moving entity but there was none");
+ }
+
+ list.removeListener(listener);
+ } else {
+ // The listener has entered the chunk
+ TrackedEntityList list = this.getOrCreateList(x, y, z);
+ list.addListener(listener);
+ }
+ }
+ }
+ }
+ }
+
+ private TrackedEntityList getOrCreateList(int x, int y, int z) {
+ return this.sections.computeIfAbsent(encode(x, y, z), TrackedEntityList::new);
+ }
+
+ private TrackedEntityList getList(int x, int y, int z) {
+ return this.sections.get(encode(x, y, z));
+ }
+
+ private static long encode(int x, int y, int z) {
+ return SectionPosition.asLong(x, y, z);
+ }
+
+ private static SectionPosition decode(long xyz) {
+ return SectionPosition.a(xyz);
+ }
+
+ private class TrackedEntityList {
+ private final Set<EntityLiving> entities = new ReferenceOpenHashSet<>();
+ private final Set<NearbyEntityListener> listeners = new ReferenceOpenHashSet<>();
+
+ private final long key;
+
+ private TrackedEntityList(long key) {
+ this.key = key;
+ }
+
+ public void addListener(NearbyEntityListener listener) {
+ for (EntityLiving entity : this.entities) {
+ listener.onEntityEnteredRange(entity);
+ }
+
+ this.listeners.add(listener);
+ }
+
+ public void removeListener(NearbyEntityListener listener) {
+ if (this.listeners.remove(listener)) {
+ for (EntityLiving entity : this.entities) {
+ listener.onEntityLeftRange(entity);
+ }
+
+ this.checkEmpty();
+ }
+ }
+
+ public boolean addTrackedEntity(EntityLiving entity) {
+ for (NearbyEntityListener listener : this.listeners) {
+ listener.onEntityEnteredRange(entity);
+ }
+
+ return this.entities.add(entity);
+ }
+
+ public boolean removeTrackedEntity(EntityLiving entity) {
+ boolean ret = this.entities.remove(entity);
+
+ if (ret) {
+ for (NearbyEntityListener listener : this.listeners) {
+ listener.onEntityLeftRange(entity);
+ }
+
+ this.checkEmpty();
+ }
+
+ return ret;
+ }
+
+ private void checkEmpty() {
+ if (this.entities.isEmpty() && this.listeners.isEmpty()) {
+ EntityTrackerEngine.this.sections.remove(this.key);
+ }
+ }
+ }
+
+
+ private static String errorMessageAlreadyListening(Reference2ReferenceOpenHashMap<NearbyEntityListener, List<TrackedEntityList>> sectionsByEntity, NearbyEntityListener listener, SectionPosition newLocation) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Adding Entity listener a second time: ").append(listener.toString());
+ builder.append("\n");
+ builder.append(" wants to listen at: ").append(newLocation.toString());
+ builder.append(" with cube radius: ").append(listener.getChunkRange());
+ builder.append("\n");
+ builder.append(" but was already listening at chunk sections: ");
+ String[] comma = new String[]{""};
+ if (sectionsByEntity.get(listener) == null) {
+ builder.append("null");
+ } else {
+ sectionsByEntity.get(listener).forEach(a -> {
+ builder.append(comma[0]);
+ builder.append(decode(a.key).toString());
+ comma[0] = ", ";
+ });
+ }
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..f84366ad98333c2b17b838883d9a3889572bba63
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java
@@ -0,0 +1,25 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import net.minecraft.world.entity.EntityLiving;
+
+/**
+ * The main interface used to receive events from the
+ * {@link me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine} of a world.
+ */
+public interface NearbyEntityListener {
+ /**
+ * Returns the range (in chunks) of this listener. This must never change during the lifetime of the listener.
+ * TODO: Allow entity listeners to change the radius they receive updates within
+ */
+ int getChunkRange();
+
+ /**
+ * Called by the entity tracker when an entity enters the range of this listener.
+ */
+ void onEntityEnteredRange(EntityLiving entity);
+
+ /**
+ * Called by the entity tracker when an entity leaves the range of this listener or is removed from the world.
+ */
+ void onEntityLeftRange(EntityLiving entity);
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java
new file mode 100644
index 0000000000000000000000000000000000000000..a56b23f0fbc1f2e31a79aa8b47635fecdf2490c9
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java
@@ -0,0 +1,59 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import net.minecraft.world.entity.EntityLiving;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Allows for multiple listeners on an entity to be grouped under one logical listener. No guarantees are made about the
+ * order of which each sub-listener will be notified.
+ */
+public class NearbyEntityListenerMulti implements NearbyEntityListener {
+ private final List<NearbyEntityListener> listeners = new ArrayList<>();
+
+ public void addListener(NearbyEntityListener listener) {
+ this.listeners.add(listener);
+ }
+
+ public void removeListener(NearbyEntityListener listener) {
+ this.listeners.remove(listener);
+ }
+
+ @Override
+ public int getChunkRange() {
+ int range = 0;
+
+ for (NearbyEntityListener listener : this.listeners) {
+ range = Math.max(range, listener.getChunkRange());
+ }
+
+ return range;
+ }
+
+ @Override
+ public void onEntityEnteredRange(EntityLiving entity) {
+ for (NearbyEntityListener listener : this.listeners) {
+ listener.onEntityEnteredRange(entity);
+ }
+ }
+
+ @Override
+ public void onEntityLeftRange(EntityLiving entity) {
+ for (NearbyEntityListener listener : this.listeners) {
+ listener.onEntityLeftRange(entity);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sublisteners = new StringBuilder();
+ String comma = "";
+ for (NearbyEntityListener listener : this.listeners) {
+ sublisteners.append(comma).append(listener.toString());
+ comma = ","; //trick to drop the first comma
+ }
+
+ return super.toString() + " with sublisteners: [" + sublisteners + "]";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java
new file mode 100644
index 0000000000000000000000000000000000000000..01f9a626e761dd8cc26216e316e3a39362dc463d
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java
@@ -0,0 +1,94 @@
+package me.jellysquid.mods.lithium.common.entity.tracker.nearby;
+
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
+import net.minecraft.world.phys.AxisAlignedBB;
+import net.minecraft.world.entity.EntityLiving;
+import net.minecraft.util.MathHelper;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition;
+
+import java.util.Set;
+
+/**
+ * Maintains a collection of all entities within the range of this listener. This allows AI goals to quickly
+ * assess nearby entities which match the provided class.
+ */
+public class NearbyEntityTracker<T extends EntityLiving> implements NearbyEntityListener {
+ private final Class<T> clazz;
+ private final EntityLiving self;
+
+ private final int rangeC;
+ private final float rangeSq;
+
+ private final Set<T> nearby = new ReferenceOpenHashSet<>();
+
+ public NearbyEntityTracker(Class<T> clazz, EntityLiving self, float range) {
+ this.clazz = clazz;
+ this.self = self;
+ this.rangeSq = range * range;
+ this.rangeC = Math.max((MathHelper.f(range) + 15) >> 4, 1);
+ }
+
+ @Override
+ public int getChunkRange() {
+ return this.rangeC;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onEntityEnteredRange(EntityLiving entity) {
+ if (!this.clazz.isInstance(entity)) {
+ return;
+ }
+
+ this.nearby.add((T) entity);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onEntityLeftRange(EntityLiving entity) {
+ if (this.nearby.isEmpty() || !this.clazz.isInstance(entity)) {
+ return;
+ }
+
+ this.nearby.remove((T) entity);
+ }
+
+ /**
+ * Gets the closest T (extends LivingEntity) to the center of this tracker that also intersects with the given box and meets the
+ * requirements of the targetPredicate.
+ * The result may be different from vanilla if there are multiple closest entities.
+ *
+ * @param box the box the entities have to intersect
+ * @param targetPredicate predicate the entity has to meet
+ * @return the closest Entity that meets all requirements (distance, box intersection, predicate, type T)
+ */
+ public T getClosestEntity(AxisAlignedBB box, PathfinderTargetCondition targetPredicate) {
+ double x = this.self.locX();
+ double y = this.self.locY();
+ double z = this.self.locZ();
+
+ T nearest = null;
+ double nearestDistance = Double.POSITIVE_INFINITY;
+
+ for (T entity : this.nearby) {
+ double distance = entity.getDistanceSquared(x, y, z);
+
+ if (distance < nearestDistance && (box == null || box.intersects(entity.getBoundingBox())) && targetPredicate.test(this.self, entity)) {
+ nearest = entity;
+ nearestDistance = distance;
+ }
+ }
+
+ if (nearestDistance <= this.rangeSq) {
+ return nearest;
+ }
+
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " for entity class: " + this.clazz.getName() + ", in rangeSq: " + this.rangeSq + ", around entity: " + this.self.toString() + " with NBT: " + this.self.save(new NBTTagCompound());
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/Collector.java b/src/main/java/me/jellysquid/mods/lithium/common/util/Collector.java
new file mode 100644
index 0000000000000000000000000000000000000000..8323c05845cd652e5ea5dd4b71c388a1f7374bf6
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/util/Collector.java
@@ -0,0 +1,11 @@
+package me.jellysquid.mods.lithium.common.util;
+
+public interface Collector<T> {
+ /**
+ * Collects the passed object and performs additional processing on it, returning a flag as to whether or not
+ * collection should continue.
+ *
+ * @return True if collection should continue, otherwise false.
+ */
+ boolean collect(T obj);
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningLong2ObjectOpenHashMap.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningLong2ObjectOpenHashMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..a94a40dcc3502d29950e5e387d658232a0bf5552
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningLong2ObjectOpenHashMap.java
@@ -0,0 +1,46 @@
+package me.jellysquid.mods.lithium.common.util.collections;
+
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+
+/**
+ * An extension for {@link Long2ObjectOpenHashMap} which allows callbacks to be installed for when an item is added to
+ * or removed from the map.
+ */
+public class ListeningLong2ObjectOpenHashMap<V> extends Long2ObjectOpenHashMap<V> {
+ private final Callback<V> addCallback, removeCallback;
+
+ public ListeningLong2ObjectOpenHashMap(Callback<V> addCallback, Callback<V> removeCallback) {
+ this.addCallback = addCallback;
+ this.removeCallback = removeCallback;
+ }
+
+ @Override
+ public V put(long k, V v) {
+ V ret = super.put(k, v);
+
+ if (ret != v) {
+ if (ret != null) {
+ this.removeCallback.apply(k, v);
+ }
+
+ this.addCallback.apply(k, v);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public V remove(long k) {
+ V ret = super.remove(k);
+
+ if (ret != null) {
+ this.removeCallback.apply(k, ret);
+ }
+
+ return ret;
+ }
+
+ public interface Callback<V> {
+ void apply(long key, V value);
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5959d66582342f614bdadb2a1ef163e4fff25341
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java
@@ -0,0 +1,18 @@
+package me.jellysquid.mods.lithium.common.world;
+
+import net.minecraft.core.BlockPosition;
+
+public class WorldHelper {
+
+ public static boolean areNeighborsWithinSameChunk(BlockPosition pos) {
+ int localX = pos.getX() & 15;
+ int localY = pos.getY() & 15;
+ int localZ = pos.getZ() & 15;
+
+ return localX > 0 && localY > 0 && localZ > 0 && localX < 15 && localY < 15 && localZ < 15;
+ }
+
+ public static boolean areAllNeighborsOutOfBounds(BlockPosition pos) {
+ return pos.getY() < -1 || pos.getY() > 256;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestCollectors.java b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestCollectors.java
new file mode 100644
index 0000000000000000000000000000000000000000..069b204764d04b126ac8ef30eae8f7e1234badf5
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestCollectors.java
@@ -0,0 +1,28 @@
+package me.jellysquid.mods.lithium.common.world.interests;
+
+import me.jellysquid.mods.lithium.common.util.Collector;
+import net.minecraft.core.BlockPosition;
+import net.minecraft.world.entity.ai.village.poi.VillagePlace;
+import net.minecraft.world.entity.ai.village.poi.VillagePlaceRecord;
+import net.minecraft.world.entity.ai.village.poi.VillagePlaceSection;
+import net.minecraft.world.entity.ai.village.poi.VillagePlaceType;
+
+import java.util.function.Predicate;
+
+public class PointOfInterestCollectors {
+ public static Collector<VillagePlaceRecord> collectAllWithinRadius(BlockPosition pos, double radius, Collector<VillagePlaceRecord> out) {
+ double radiusSq = radius * radius;
+
+ return (point) -> {
+ if (point.getPosition().distanceSquared(pos) <= radiusSq) {
+ return out.collect(point);
+ }
+
+ return true;
+ };
+ }
+
+ public static Collector<VillagePlaceSection> collectAllMatching(Predicate<VillagePlaceType> predicate, VillagePlace.Occupancy status, Collector<VillagePlaceRecord> out) {
+ return set -> set.get(predicate, status, out);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestTypeHelper.java b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestTypeHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..06d7c8a5ae23ec8b55661b4129c7c88657d1b9bf
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestTypeHelper.java
@@ -0,0 +1,22 @@
+package me.jellysquid.mods.lithium.common.world.interests;
+
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+import net.minecraft.world.level.chunk.ChunkSection;
+import net.minecraft.world.level.block.state.IBlockData;
+
+public class PointOfInterestTypeHelper {
+ private static ObjectSet<IBlockData> TYPES;
+
+ public static void init(ObjectSet<IBlockData> types) {
+ if (TYPES != null) {
+ throw new IllegalStateException("Already initialized");
+ }
+
+ TYPES = types;
+ }
+
+ public static boolean shouldScan(ChunkSection section) {
+ return section.hasAny(TYPES::contains);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/minecraft/core/SectionPosition.java b/src/main/java/net/minecraft/core/SectionPosition.java
index a206a729b3afa01bf591fa4da1e5c14902da4778..716f91246c4a45fd49af806afd1781f11ff3b346 100644
--- a/src/main/java/net/minecraft/core/SectionPosition.java
+++ b/src/main/java/net/minecraft/core/SectionPosition.java
@@ -22,6 +22,7 @@ public class SectionPosition extends BaseBlockPosition {
return new SectionPosition(blockposition.getX() >> 4, blockposition.getY() >> 4, blockposition.getZ() >> 4); // Paper
}
+ public static SectionPosition from(ChunkCoordIntPair chunkCoordIntPair, int i){ return a(chunkCoordIntPair, i); } // Yatopia - OBFHELPER
public static SectionPosition a(ChunkCoordIntPair chunkcoordintpair, int i) {
return new SectionPosition(chunkcoordintpair.x, i, chunkcoordintpair.z);
}
@@ -94,14 +95,17 @@ public class SectionPosition extends BaseBlockPosition {
return i << 4;
}
+ public static int unpackX(long i){ return b(i); } // Yatopia - OBFHELPER
public static int b(long i) {
return (int) (i << 0 >> 42);
}
+ public static int unpackY(long i){ return c(i); } // Yatopia - OBFHELPER
public static int c(long i) {
return (int) (i << 44 >> 44);
}
+ public static int unpackZ(long i){ return d(i); } // Yatopia - OBFHELPER
public static int d(long i) {
return (int) (i << 22 >> 42);
}
diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java
2021-04-21 23:26:49 +02:00
index d7f95bd32842fdee0ce53fc97d31ffb3577cdc78..e6f85b07f731859c8b8c380afad699ccc1d6c3e8 100644
2021-04-18 02:13:30 +02:00
--- a/src/main/java/net/minecraft/server/level/WorldServer.java
+++ b/src/main/java/net/minecraft/server/level/WorldServer.java
@@ -177,6 +177,7 @@ import org.bukkit.event.world.TimeSkipEvent;
// CraftBukkit end
import it.unimi.dsi.fastutil.ints.IntArrayList; // Tuinity
import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia
+import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine; // Yatopia
public class WorldServer extends World implements GeneratorAccessSeed, NonBlockingWorldAccess { // Yatopia
@@ -2031,6 +2032,16 @@ public class WorldServer extends World implements GeneratorAccessSeed, NonBlocki
while (iterator.hasNext()) {
Entity entity = (Entity) iterator.next();
+ if (entity instanceof EntityLiving) { // Yatopia start - Port lithium
+
+ int chunkX = MathHelper.floor(entity.locX()) >> 4;
+ int chunkY = MathHelper.clamp(MathHelper.floor(entity.locY()) >> 4, 0, 15);
+ int chunkZ = MathHelper.floor(entity.locZ()) >> 4;
+
+ EntityTrackerEngine tracker = this.getEntityTracker();
+ tracker.onEntityRemoved(chunkX, chunkY, chunkZ, (EntityLiving) entity);
+ } // Yatopia End
+
if (!(entity instanceof EntityPlayer)) {
if (false && this.tickingEntities) { // Tuinity
throw (IllegalStateException) SystemUtils.c((Throwable) (new IllegalStateException("Removing entity while ticking!")));
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index cd1f94e5c1c923ee9d8dd451406ac2bee360e9c3..7befe4263a2d046922438e1a9853f2d8290ee230 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -177,6 +177,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
private CraftEntity bukkitEntity;
public PlayerChunkMap.EntityTracker tracker; // Paper package private -> public
+ public PlayerChunkMap.EntityTracker getTracker() { return tracker; } // Yatopia
public boolean collisionLoadChunks = false; // Paper
public Throwable addedToWorldStack; // Paper - entity debug
public CraftEntity getBukkitEntity() {
diff --git a/src/main/java/net/minecraft/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java
2021-04-21 23:26:49 +02:00
index a057be22f488f76b926c81dc5c63e7f9c3fb54a1..a897ad3de8c18be8bb3c320950a7421477040e8a 100644
2021-04-18 02:13:30 +02:00
--- a/src/main/java/net/minecraft/world/entity/EntityLiving.java
+++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java
@@ -141,9 +141,11 @@ import org.bukkit.event.player.PlayerItemConsumeEvent;
// CraftBukkit end
import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListenerMulti; // Yatopia
public abstract class EntityLiving extends Entity {
+ private NearbyEntityListenerMulti tracker; // Yatopia - Port lithium
private static final UUID b = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D");
private static final UUID c = UUID.fromString("87f46a96-686f-4796-b035-22e16ee9e038");
private static final AttributeModifier d = new AttributeModifier(EntityLiving.b, "Sprinting speed boost", 0.30000001192092896D, AttributeModifier.Operation.MULTIPLY_TOTAL);
@@ -277,8 +279,14 @@ public abstract class EntityLiving extends Entity {
DynamicOpsNBT dynamicopsnbt = DynamicOpsNBT.a;
this.bg = this.a(new Dynamic(dynamicopsnbt, dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), dynamicopsnbt.emptyMap()))));
+ this.tracker = new NearbyEntityListenerMulti(); // Yatopia - Port lithium
}
+ // Yatopia start - Port lithium
+ public NearbyEntityListenerMulti getListener() {
+ return this.tracker;
+ }
+ // Yatopia end
protected void initAttributes() {} // Purpur
public BehaviorController<?> getBehaviorController() {
diff --git a/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java b/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java
index e38cdf81e3f3ba1a2ee86c3a202e5643dd9c0fd1..48524cdf7a1a5d4cff12b24b11dbea9935c85131 100644
--- a/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java
+++ b/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java
@@ -223,6 +223,7 @@ public class BehaviorController<E extends EntityLiving> {
}).isPresent();
}
+ public boolean isMemoryInState(MemoryModuleType<?> memorymoduletype, MemoryStatus memorystatus){ return a(memorymoduletype, memorystatus); } // Yatopia - OBFHELPER
public boolean a(MemoryModuleType<?> memorymoduletype, MemoryStatus memorystatus) {
Optional<? extends ExpirableMemory<?>> optional = (Optional) this.memories.get(memorymoduletype);
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
index ed3a3593b417131837341784b09cb3f9f76a44be..922f7bf57e65b0bab18b9044ff6467a2660aa992 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
@@ -1,5 +1,8 @@
package net.minecraft.world.entity.ai.behavior;
+import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
+import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
+import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
@@ -10,7 +13,7 @@ import net.minecraft.world.entity.ai.memory.MemoryStatus;
public abstract class Behavior<E extends EntityLiving> {
- protected final Map<MemoryModuleType<?>, MemoryStatus> a;
+ protected final Map<MemoryModuleType<?>, MemoryStatus> a; protected final Map<MemoryModuleType<?>, MemoryStatus> getRequiredMemoryStates(){ return a; } // Yatopia - OBFHELPER
private Behavior.Status b; public final Behavior.Status getStatus() { return this.b; } // Tuinity - OBFHELPER
private long c;
co.aikar.timings.Timing timing; // Origami - behavior timing
@@ -29,7 +32,7 @@ public abstract class Behavior<E extends EntityLiving> {
this.b = Behavior.Status.STOPPED;
this.d = i;
this.e = j;
- this.a = map;
+ this.a = new Reference2ObjectOpenHashMap<>(map); // Yatopia - Port lithium
String key = getClass().getSimpleName(); // Yatopia Compatible Fix
// Origami start - behavior timing
timing = co.aikar.timings.WorldTimingsHandler.getBehaviorTimings(key);
@@ -40,6 +43,7 @@ public abstract class Behavior<E extends EntityLiving> {
return this.b;
}
+ public final boolean tryStarting(WorldServer worldserver, E e0, long i){ return e(worldserver, e0, i); } // Yatopia - OBFHELPER
public final boolean e(WorldServer worldserver, E e0, long i) {
if (this.a(e0) && this.a(worldserver, e0)) {
this.b = Behavior.Status.RUNNING;
@@ -92,23 +96,17 @@ public abstract class Behavior<E extends EntityLiving> {
}
private boolean a(E e0) {
- Iterator iterator = this.a.entrySet().iterator();
+ // Yatopia start - port lithium
+ Iterable<Reference2ObjectMap.Entry<MemoryModuleType<?>, MemoryStatus>> iterable =
+ Reference2ObjectMaps.fastIterable((Reference2ObjectOpenHashMap<MemoryModuleType<?>, MemoryStatus>)this.getRequiredMemoryStates());
- MemoryModuleType memorymoduletype;
- MemoryStatus memorystatus;
-
- do {
- if (!iterator.hasNext()) {
- return true;
+ for (Reference2ObjectMap.Entry<MemoryModuleType<?>, MemoryStatus> entry : iterable) {
+ if (!e0.getBehaviorController().isMemoryInState(entry.getKey(), entry.getValue())) {
+ return false;
}
+ }
- Entry<MemoryModuleType<?>, MemoryStatus> entry = (Entry) iterator.next();
-
- memorymoduletype = (MemoryModuleType) entry.getKey();
- memorystatus = (MemoryStatus) entry.getValue();
- } while (e0.getBehaviorController().a(memorymoduletype, memorystatus));
-
- return false;
+ return true; // Yatopia end
}
public static enum Status {
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java b/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java
index c46cdffe3d877bff70b843766c8189eae06148ff..9cf43a86b573ad2c553956c081ae40820cbc7d5e 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java
@@ -31,17 +31,15 @@ public class BehaviorGate<E extends EntityLiving> extends Behavior<E> {
@Override
protected boolean b(WorldServer worldserver, E e0, long i) {
- // Tuinity start - remove streams
- List<WeightedList.a<Behavior<? super E>>> list = this.getList().getList();
- for (int index = 0, len = list.size(); index < len; ++index) {
- Behavior<? super E> behavior = list.get(index).getValue();
+ // Tuinity start - remove streams // Yatopia start - Port lithium
+ for (Behavior<? super E> behavior : this.getList()) {
if (behavior.getStatus() == Status.RUNNING && behavior.b(worldserver, e0, i)) { // copied from removed code, make sure to update
return true;
}
}
return false;
- // Tuinity end - remove streams
+ // Tuinity end - remove streams // Yatopia start - Port lithium
}
@Override
@@ -57,23 +55,19 @@ public class BehaviorGate<E extends EntityLiving> extends Behavior<E> {
@Override
protected void d(WorldServer worldserver, E e0, long i) {
- // Tuinity start - remove streams
- List<WeightedList.a<Behavior<? super E>>> list = this.getList().getList();
- for (int index = 0, len = list.size(); index < len; ++index) {
- Behavior<? super E> behavior = list.get(index).getValue();
+ // Tuinity start - remove streams // Yatopia start - Port lithium
+ for (Behavior<? super E> behavior : this.getList()) {
if (behavior.getStatus() == Behavior.Status.RUNNING) {
behavior.f(worldserver, e0, i); // copied from removed code, make sure to update
}
}
- // Tuinity end - remove streams
+ // Tuinity end - remove streams // Yatopia end
}
@Override
protected void c(WorldServer worldserver, E e0, long i) {
- // Tuinity start - remove streams
- List<WeightedList.a<Behavior<? super E>>> list = this.getList().getList();
- for (int index = 0, len = list.size(); index < len; ++index) {
- Behavior<? super E> behavior = list.get(index).getValue();
+ // Tuinity start - remove streams// Yatopia start - Port lithium
+ for (Behavior<? super E> behavior : this.getList()) {
if (behavior.getStatus() == Behavior.Status.RUNNING) {
behavior.g(worldserver, e0, i); // copied from removed code, make sure to update
}
@@ -81,7 +75,9 @@ public class BehaviorGate<E extends EntityLiving> extends Behavior<E> {
// Tuinity end - remove streams
BehaviorController behaviorcontroller = e0.getBehaviorController();
- this.b.forEach(behaviorcontroller::removeMemory); // Paper - decomp fix
+ for(MemoryModuleType<?> moduleType : this.b){
+ behaviorcontroller.removeMemory(moduleType);
+ } // Yatopia end
}
@Override
@@ -98,29 +94,25 @@ public class BehaviorGate<E extends EntityLiving> extends Behavior<E> {
RUN_ONE {
@Override
public <E extends EntityLiving> void a(WeightedList<Behavior<? super E>> weightedlist, WorldServer worldserver, E e0, long i) {
- // Tuinity start - remove streams
- List<WeightedList.a<Behavior<? super E>>> list = weightedlist.getList();
- for (int index = 0, len = list.size(); index < len; ++index) {
- Behavior<? super E> behavior = list.get(index).getValue();
- if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.e(worldserver, e0, i)) { // copied from removed code, make sure to update
+ // Tuinity start - remove streams // Yatopia start - port lithium
+ for (Behavior<? super E> behavior : weightedlist) {
+ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStarting(worldserver, e0, i)) { // copied from removed code, make sure to update
break;
}
}
- // Tuinity end - remove streams
+ // Tuinity end - remove streams // Yatopia end
}
},
TRY_ALL {
@Override
public <E extends EntityLiving> void a(WeightedList<Behavior<? super E>> weightedlist, WorldServer worldserver, E e0, long i) {
- // Tuinity start - remove streams
- List<WeightedList.a<Behavior<? super E>>> list = weightedlist.getList();
- for (int index = 0, len = list.size(); index < len; ++index) {
- Behavior<? super E> behavior = list.get(index).getValue();
+ // Tuinity start - remove streams // Yatopia start - port lithium
+ for (Behavior<? super E> behavior : weightedlist) {
if (behavior.getStatus() == Behavior.Status.STOPPED) {
- behavior.e(worldserver, e0, i); // copied from removed code, make sure to update
+ behavior.tryStarting(worldserver, e0, i); // copied from removed code, make sure to update
}
}
- // Tuinity end - remove streams
+ // Tuinity end - remove streams // Yatopia end
}
};
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java
index e2b5d6155bebdbf99b0850de7f9e1f5d342f9e2f..a3236e6359a2e72b4a41be4717780c20e2a31af3 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java
@@ -8,11 +8,12 @@ import com.mojang.serialization.Dynamic;
import com.mojang.serialization.DynamicOps;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
-public class WeightedList<U> {
+public class WeightedList<U> implements Iterable<U> { // Yatopia - Port lithium
protected final List<WeightedList.a<U>> list; public final List<WeightedList.a<U>> getList() { return this.list; } // Paper - decompile conflict // Tuinity - OBFHELPER
private final Random b;
@@ -116,4 +117,35 @@ public class WeightedList<U> {
};
}
}
-}
+
+
+ // Yatopia start - Port lithium
+ /**
+ * A wrapper type for an iterator over the entries of a {@link WeightedList} which de-references the contained
+ * values for consumers.
+ *
+ * @param <U> The value type stored in each list entry
+ */
+ class ListIterator<U> implements Iterator<U> {
+ private final Iterator<WeightedList.a<U>> inner;
+
+ public ListIterator(Iterator<WeightedList.a<U>> inner) {
+ this.inner = inner;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.inner.hasNext();
+ }
+
+ @Override
+ public U next() {
+ return this.inner.next().getValue();
+ }
+ }
+
+ @Override
+ public Iterator<U> iterator() {
+ return new ListIterator<>(this.list.iterator());
+ }
+} // Yatopia End
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java
index 35502bd2f7d9cebf5cfe1060e300a5032dbe6a5d..eea1a396f06e8feaa5637ba4e589a13169f514da 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java
@@ -1,5 +1,7 @@
package net.minecraft.world.entity.ai.goal;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityTracker;
+
import java.util.EnumSet;
import java.util.function.Predicate;
import net.minecraft.world.entity.Entity;
@@ -25,6 +27,7 @@ public class PathfinderGoalAvoidTarget<T extends EntityLiving> extends Pathfinde
protected final Predicate<EntityLiving> g;
protected final Predicate<EntityLiving> h;
private final PathfinderTargetCondition k;
+ private NearbyEntityTracker<T> tracker; // Yatopia - Port Lithium
public PathfinderGoalAvoidTarget(EntityCreature entitycreature, Class<T> oclass, float f, double d0, double d1) {
this(entitycreature, oclass, entityliving -> true, f, d0, d1, IEntitySelector.e::test); // Purpur - decompile fix
@@ -41,6 +44,10 @@ public class PathfinderGoalAvoidTarget<T extends EntityLiving> extends Pathfinde
this.e = entitycreature.getNavigation();
this.a(EnumSet.of(PathfinderGoal.Type.MOVE));
this.k = (new PathfinderTargetCondition()).a((double) f).a(predicate1.and(predicate));
+ // Yatopia start - Port Lithium
+ this.tracker = new NearbyEntityTracker<>(oclass, entitycreature, f);
+ entitycreature.getListener().addListener(this.tracker);
+ // Yatopia end
}
public PathfinderGoalAvoidTarget(EntityCreature entitycreature, Class<T> oclass, float f, double d0, double d1, Predicate<EntityLiving> predicate) {
@@ -51,7 +58,7 @@ public class PathfinderGoalAvoidTarget<T extends EntityLiving> extends Pathfinde
@Override
public boolean a() {
- this.b = this.a.world.b(this.f, this.k, this.a, this.a.locX(), this.a.locY(), this.a.locZ(), this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c));
+ this.b = this.tracker.getClosestEntity(this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c), this.k); // Yatopia - Port lithium
if (this.b == null) {
return false;
} else {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java
index 3bcbad5e298cf05c1b41476a08a3a69cb7fdf79f..8b41274aa388a758bd8acf9637e2a19caf744c57 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java
@@ -1,5 +1,6 @@
package net.minecraft.world.entity.ai.goal;
+import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityTracker;
import java.util.EnumSet;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityInsentient;
@@ -10,6 +11,7 @@ import net.minecraft.world.entity.player.EntityHuman;
public class PathfinderGoalLookAtPlayer extends PathfinderGoal {
+ private NearbyEntityTracker<? extends EntityLiving> tracker; // Yatopia - Port lithium
protected final EntityInsentient a;
protected Entity b;
protected final float c;
@@ -27,7 +29,7 @@ public class PathfinderGoalLookAtPlayer extends PathfinderGoal {
this.e = oclass;
this.c = f;
this.d = f1;
- this.a(EnumSet.of(PathfinderGoal.Type.LOOK));
+ this.a(EnumSet.of(Type.LOOK)); // Yatopia - Port lithium
if (oclass == EntityHuman.class) {
this.f = (new PathfinderTargetCondition()).a((double) f).b().a().d().a((entityliving) -> {
return IEntitySelector.b(entityinsentient).test(entityliving);
@@ -35,7 +37,10 @@ public class PathfinderGoalLookAtPlayer extends PathfinderGoal {
} else {
this.f = (new PathfinderTargetCondition()).a((double) f).b().a().d();
}
-
+ // Yatopia start - Port lithium
+ this.tracker = new NearbyEntityTracker<>(oclass, entityinsentient, f);
+ entityinsentient.getListener().addListener(this.tracker);
+ // Yatopia end
}
@Override
@@ -48,9 +53,9 @@ public class PathfinderGoalLookAtPlayer extends PathfinderGoal {
}
if (this.e == EntityHuman.class) {
- this.b = this.a.world.a(this.f, this.a, this.a.locX(), this.a.getHeadY(), this.a.locZ());
+ this.b = this.tracker.getClosestEntity(null, this.f); // Yatopia - Port lithium
} else {
- this.b = this.a.world.b(this.e, this.f, this.a, this.a.locX(), this.a.getHeadY(), this.a.locZ(), this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c));
+ this.b = this.tracker.getClosestEntity(this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c), this.f); // Yatopia - Port lithium
}
return this.b != null;
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java
index 29cd71efe86eea2227f373c15c39dc530e9e8199..7dcd055b37bb7051127e10f5d191d23e0562b29e 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java
@@ -7,7 +7,12 @@ import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Tuinity
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
+import me.jellysquid.mods.lithium.common.util.Collector;
+import me.jellysquid.mods.lithium.common.world.interests.PointOfInterestCollectors;
+import me.jellysquid.mods.lithium.common.world.interests.PointOfInterestTypeHelper;
+
import java.io.File;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -193,7 +198,7 @@ public class VillagePlace extends RegionFileSection<VillagePlaceSection> {
public long count(Predicate<VillagePlaceType> predicate, BlockPosition blockposition, int i, VillagePlace.Occupancy villageplace_occupancy) { return a(predicate, blockposition, i, villageplace_occupancy); } // Purpur - OBFHELPER
public long a(Predicate<VillagePlaceType> predicate, BlockPosition blockposition, int i, VillagePlace.Occupancy villageplace_occupancy) {
- return this.cList(predicate, blockposition, i, villageplace_occupancy).size(); // Yatopia
+ return this.getAllWithinCircle(predicate, blockposition, i, villageplace_occupancy).size(); // Yatopia
}
public boolean a(VillagePlaceType villageplacetype, BlockPosition blockposition) {
@@ -236,6 +241,35 @@ public class VillagePlace extends RegionFileSection<VillagePlaceSection> {
return ret;
}
+ private List<VillagePlaceRecord> getAllWithinCircle(Predicate<VillagePlaceType> predicate, BlockPosition pos, int radius, Occupancy status) {
+ List<VillagePlaceRecord> points = new ArrayList<>();
+
+ this.collectWithinCircle(predicate, pos, radius, status, points::add);
+
+ return points;
+ }
+
+ private void collectWithinCircle(Predicate<VillagePlaceType> predicate, BlockPosition pos, int radius, Occupancy status, Collector<VillagePlaceRecord> collector) {
+ Collector<VillagePlaceRecord> filter = PointOfInterestCollectors.collectAllWithinRadius(pos, radius, collector);
+ Collector<VillagePlaceSection> consumer = PointOfInterestCollectors.collectAllMatching(predicate, status, filter);
+
+ int minChunkX = (pos.getX() - radius - 1) >> 4;
+ int minChunkZ = (pos.getZ() - radius - 1) >> 4;
+
+ int maxChunkX = (pos.getX() + radius + 1) >> 4;
+ int maxChunkZ = (pos.getZ() + radius + 1) >> 4;
+
+ // noinspection unchecked
+
+ for (int x = minChunkX; x <= maxChunkX; x++) {
+ for (int z = minChunkZ; z <= maxChunkZ; z++) {
+ if (!this.collectWithinChunkColumn(x, z, consumer)) {
+ return;
+ }
+ }
+ }
+ }
+
public java.util.List<BlockPosition> cListPositions(Predicate<VillagePlaceType> predicate, Predicate<VillagePlaceRecord> recordFilter, BlockPosition pos, int i, VillagePlace.Occupancy occupancy) {
int j = i * i;
java.util.List<BlockPosition> ret = new java.util.ArrayList<>();
@@ -255,12 +289,12 @@ public class VillagePlace extends RegionFileSection<VillagePlaceSection> {
});
}
+ /**
+ * @reason Retrieve all points of interest in one operation
+ * @author JellySquid
+ */
public Stream<VillagePlaceRecord> a(Predicate<VillagePlaceType> predicate, ChunkCoordIntPair chunkcoordintpair, VillagePlace.Occupancy villageplace_occupancy) {
- return IntStream.range(0, 16).boxed().map((integer) -> {
- return this.d(SectionPosition.a(chunkcoordintpair, integer).s());
- }).filter(Optional::isPresent).flatMap((optional) -> {
- return ((VillagePlaceSection) optional.get()).a(predicate, villageplace_occupancy);
- });
+ return this.getWithinChunkColumn(chunkcoordintpair.x, chunkcoordintpair.z).flatMap((set) -> set.get(predicate, villageplace_occupancy));
}
// Yatopia start
@@ -273,12 +307,16 @@ public class VillagePlace extends RegionFileSection<VillagePlaceSection> {
}
return ret;
}
- // Yatopia end
+
+ /**
+ * @reason Retrieve all points of interest in one operation
+ * @author JellySquid
+ */
public Stream<BlockPosition> a(Predicate<VillagePlaceType> predicate, Predicate<BlockPosition> predicate1, BlockPosition blockposition, int i, VillagePlace.Occupancy villageplace_occupancy) {
return this.c(predicate, blockposition, i, villageplace_occupancy).map(VillagePlaceRecord::f).filter(predicate1);
}
- // Yatopia start
+
public java.util.List<BlockPosition> bList(Predicate<VillagePlaceType> predicate, Predicate<BlockPosition> posFilter, BlockPosition pos, int i, VillagePlace.Occupancy occupancy) {
java.util.List<BlockPosition> ret = aList(predicate, posFilter, pos, i, occupancy);
ret.sort(Comparator.comparingDouble((pos1) -> pos1.distanceSquared(pos)));
@@ -404,24 +442,28 @@ public class VillagePlace extends RegionFileSection<VillagePlaceSection> {
this.updateDistanceTracking(i); // Tuinity - move to new distance tracking util
}
+ /**
+ * @reason Avoid Stream API
+ * @author Jellysquid
+ */
public void a(ChunkCoordIntPair chunkcoordintpair, ChunkSection chunksection) {
- SectionPosition sectionposition = SectionPosition.a(chunkcoordintpair, chunksection.getYPosition() >> 4);
+ SectionPosition sectionPos = SectionPosition.from(chunkcoordintpair, chunksection.getYPosition() >> 4);
- SystemUtils.a(this.d(sectionposition.s()), (villageplacesection) -> {
- villageplacesection.a((biconsumer) -> {
- if (a(chunksection)) {
- this.a(chunksection, sectionposition, biconsumer);
- }
+ VillagePlaceSection set = this.get(sectionPos.asLong()).orElse(null);
+ if (set != null) {
+ set.refresh((consumer) -> {
+ if (PointOfInterestTypeHelper.shouldScan(chunksection)) {
+ this.scanAndPopulate(chunksection, sectionPos, consumer);
+ }
});
- }, () -> {
- if (a(chunksection)) {
- VillagePlaceSection villageplacesection = (VillagePlaceSection) this.e(sectionposition.s());
+ } else {
+ if (PointOfInterestTypeHelper.shouldScan(chunksection)) {
+ set = this.getOrCreate(sectionPos.asLong());
- this.a(chunksection, sectionposition, villageplacesection::a);
+ this.scanAndPopulate(chunksection, sectionPos, set::add);
}
-
- });
+ }
}
private static boolean a(ChunkSection chunksection) {
@@ -431,6 +473,7 @@ public class VillagePlace extends RegionFileSection<VillagePlaceSection> {
return chunksection.a(set::contains);
}
+ private void scanAndPopulate(ChunkSection chunksection, SectionPosition sectionposition, BiConsumer<BlockPosition, VillagePlaceType> biconsumer){ a(chunksection, sectionposition, biconsumer); } // Yatopia - OBFHELPER
private void a(ChunkSection chunksection, SectionPosition sectionposition, BiConsumer<BlockPosition, VillagePlaceType> biconsumer) {
sectionposition.tList().forEach((blockposition) -> { // Yatopia
IBlockData iblockdata = chunksection.getType(SectionPosition.b(blockposition.getX()), SectionPosition.b(blockposition.getY()), SectionPosition.b(blockposition.getZ()));
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java
index 866e9a434423702d2edaf9b52fa0e6219e13c2d7..640aee2b8afd66082cc867b14980f06d9811fc28 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java
@@ -26,6 +26,7 @@ import net.minecraft.core.SectionPosition;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Supplier;
+import me.jellysquid.mods.lithium.common.util.Collector;
public class VillagePlaceSection {
@@ -73,7 +74,28 @@ public class VillagePlaceSection {
}
return ret;
}
+
+ public boolean get(Predicate<VillagePlaceType> type, VillagePlace.Occupancy status, Collector<VillagePlaceRecord> consumer) {
+ for (Map.Entry<VillagePlaceType, Set<VillagePlaceRecord>> entry : this.getData().entrySet()) {
+ if (!type.test(entry.getKey())) {
+ continue;
+ }
+
+ for (VillagePlaceRecord poi : entry.getValue()) {
+ if (!status.getPredicate().test(poi)) {
+ continue;
+ }
+
+ if (!consumer.collect(poi)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
// Yatopia end
+ public Stream<VillagePlaceRecord> get(Predicate<VillagePlaceType> predicate, VillagePlace.Occupancy villageplace_occupancy){ return a(predicate, villageplace_occupancy); } // Yatopia - OBFHELPER
public Stream<VillagePlaceRecord> a(Predicate<VillagePlaceType> predicate, VillagePlace.Occupancy villageplace_occupancy) {
return this.c.entrySet().stream().filter((entry) -> {
return predicate.test(entry.getKey());
@@ -82,6 +104,7 @@ public class VillagePlaceSection {
}).filter(villageplace_occupancy.a());
}
+ public void add(BlockPosition blockPosition, VillagePlaceType villagePlaceType) { a(blockPosition, villagePlaceType); } // Yatopia - OBFHELPER
public void a(BlockPosition blockposition, VillagePlaceType villageplacetype) {
if (this.a(new VillagePlaceRecord(blockposition, villageplacetype, this.d))) {
VillagePlaceSection.LOGGER.debug("Added POI of type {} @ {}", new Supplier[]{() -> {
@@ -160,6 +183,7 @@ public class VillagePlaceSection {
return villageplacerecord != null ? Optional.of(villageplacerecord.g()) : Optional.empty();
}
+ public void refresh(Consumer<BiConsumer<BlockPosition, VillagePlaceType>> consumer){ a(consumer); } // Yatopia - OBFHELPER
public void a(Consumer<BiConsumer<BlockPosition, VillagePlaceType>> consumer) {
if (!this.e) {
Short2ObjectMap<VillagePlaceRecord> short2objectmap = new Short2ObjectOpenHashMap(this.b);
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java
index 4af526ecbed506161cb021ea320b0f21112d7bf0..4a7b3750f1b3ce3143053215dbca6da6eee052bd 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java
@@ -4,7 +4,11 @@ import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
+import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
+import me.jellysquid.mods.lithium.common.world.interests.PointOfInterestTypeHelper;
+
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -46,7 +50,8 @@ public class VillagePlaceType {
}).filter((iblockdata) -> {
return iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.HEAD;
}).collect(ImmutableSet.toImmutableSet());
- private static final Map<IBlockData, VillagePlaceType> A = Maps.newHashMap();
+ private static final Map<IBlockData, VillagePlaceType> A = new Reference2ReferenceOpenHashMap<>(); // Yatopia - Port Lithium
+ public static final Map<IBlockData, VillagePlaceType> getBlockStateToPointOfInterestType(){ return A; } // Yatopia - OBFHELPER
public static final VillagePlaceType c = a("unemployed", ImmutableSet.of(), 1, VillagePlaceType.a, 1);
public static final VillagePlaceType d = a("armorer", a(Blocks.BLAST_FURNACE), 1, 1);
public static final VillagePlaceType e = a("butcher", a(Blocks.SMOKER), 1, 1);
@@ -75,6 +80,12 @@ public class VillagePlaceType {
private final Predicate<VillagePlaceType> E;
private final int F;
+ // Yatopia Start - Port lithium
+ static {
+ PointOfInterestTypeHelper.init(new ObjectArraySet<>(getBlockStateToPointOfInterestType().keySet()));
+ }
+ // Yatopia End
+
private static Set<IBlockData> a(Block block) {
return ImmutableSet.copyOf(block.getStates().a());
}
diff --git a/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java b/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java
index 8eec32af12c69e1963dcd304a25ec4811b2f1f5a..e4bb472002934f749ff0e2f43744c73fcd74d3cc 100644
--- a/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java
+++ b/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java
@@ -47,9 +47,10 @@ import net.minecraft.world.phys.Vec3D;
public abstract class EntityRaider extends EntityMonsterPatrolling {
+ private static final ItemStack CACHED_OMINOUS_BANNER = Raid.getOminousBanner(); // Yatopia - Port lithium
protected static final DataWatcherObject<Boolean> c = DataWatcher.a(EntityRaider.class, DataWatcherRegistry.i);
private static final Predicate<EntityItem> b = (entityitem) -> {
- return !entityitem.p() && entityitem.isAlive() && ItemStack.matches(entityitem.getItemStack(), Raid.s());
+ return !entityitem.p() && entityitem.isAlive() && ItemStack.matches(entityitem.getItemStack(), CACHED_OMINOUS_BANNER); // Yatopia - Port lithium
};
@Nullable
protected Raid d;
@@ -151,7 +152,7 @@ public abstract class EntityRaider extends EntityMonsterPatrolling {
}
}
- if (!itemstack.isEmpty() && ItemStack.matches(itemstack, Raid.s()) && entityhuman != null) {
+ if (!itemstack.isEmpty() && ItemStack.matches(itemstack, CACHED_OMINOUS_BANNER) && entityhuman != null) { // Yatopia - Port lithium
MobEffect mobeffect = entityhuman.getEffect(MobEffects.BAD_OMEN);
byte b0 = 1;
int i;
@@ -242,7 +243,7 @@ public abstract class EntityRaider extends EntityMonsterPatrolling {
ItemStack itemstack = entityitem.getItemStack();
boolean flag = this.fb() && this.fa().b(this.fc()) != null;
- if (this.fb() && !flag && ItemStack.matches(itemstack, Raid.s())) {
+ if (this.fb() && !flag && ItemStack.matches(itemstack, CACHED_OMINOUS_BANNER)) { // Yatopia - Port lithium
EnumItemSlot enumitemslot = EnumItemSlot.HEAD;
ItemStack itemstack1 = this.getEquipment(enumitemslot);
double d0 = (double) this.e(enumitemslot);
@@ -535,7 +536,7 @@ public abstract class EntityRaider extends EntityMonsterPatrolling {
if ((!getRaider().world.purpurConfig.pillagerBypassMobGriefing && !getRaider().world.getGameRules().getBoolean(GameRules.MOB_GRIEFING)) || !getRaider().canPickupLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur
Raid raid = this.b.fa();
- if (this.b.fb() && !this.b.fa().a() && this.b.eN() && !ItemStack.matches(this.b.getEquipment(EnumItemSlot.HEAD), Raid.s())) {
+ if (this.b.fb() && !this.b.fa().a() && this.b.eN() && !ItemStack.matches(this.b.getEquipment(EnumItemSlot.HEAD), CACHED_OMINOUS_BANNER)) { // Yatopia - Port lithium
EntityRaider entityraider = raid.b(this.b.fc());
if (entityraider == null || !entityraider.isAlive()) {
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java
index f9d03b6d11ad5ffbfe5be072e8631f046bcd1646..52def6de2225cf5ed70c807b74fb5c2ccd133503 100644
--- a/src/main/java/net/minecraft/world/entity/raid/Raid.java
+++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java
@@ -227,8 +227,14 @@ public class Raid {
this.status = Raid.Status.STOPPED;
}
+ private boolean isBarDirty; // Yatopia - Port lithium
public void o() {
if (!this.isStopped()) {
+ if (this.isBarDirty) { // Yatopia Start - Port lithium
+ this.bossBattle.setProgress(MathHelper.a(this.sumMobHealth() / this.totalHealth, 0.0F, 1.0F));
+
+ this.isBarDirty = false;
+ } // Yatopia End
if (this.status == Raid.Status.ONGOING) {
boolean flag = this.active;
@@ -596,7 +602,7 @@ public class Raid {
}
public void updateProgress() {
- this.bossBattle.setProgress(MathHelper.a(this.sumMobHealth() / this.totalHealth, 0.0F, 1.0F));
+ this.isBarDirty = true; // Yatopia - Port lithium
}
public float sumMobHealth() {
@@ -647,6 +653,7 @@ public class Raid {
this.world.getPersistentRaid().b();
}
+ public static ItemStack getOminousBanner(){ return s(); } // Yatopia - OBFHELPER
public static ItemStack s() {
ItemStack itemstack = new ItemStack(Items.WHITE_BANNER);
NBTTagCompound nbttagcompound = itemstack.a("BlockEntityTag");
diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java
index f5badbe0dee5c40cf83a5d2993d27ed70ddd2c85..db2b1863b7f46be53839fb3e86870745fb7567fd 100644
--- a/src/main/java/net/minecraft/world/level/World.java
+++ b/src/main/java/net/minecraft/world/level/World.java
@@ -97,6 +97,7 @@ import org.bukkit.event.block.BlockPhysicsEvent;
// CraftBukkit end
import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia
+import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine; // Yatopia
public abstract class World implements GeneratorAccess, AutoCloseable, NonBlockingWorldAccess { // Yatopia
@@ -291,6 +292,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki
}
// Tuinity end - optimise checkDespawn
+ private EntityTrackerEngine tracker; // Yatopia - Port lithium
protected World(WorldDataMutable worlddatamutable, ResourceKey<World> resourcekey, final DimensionManager dimensionmanager, Supplier<GameProfilerFiller> supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.WorldDataServer) worlddatamutable).getName()); // Spigot
@@ -368,8 +370,14 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki
this.keepSpawnInMemory = this.paperConfig.keepSpawnInMemory; // Paper
this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime);
this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime);
+ this.tracker = new EntityTrackerEngine(); // Yatopia - Port lithium
}
+ // Yatopia start - Port lithium
+ public EntityTrackerEngine getEntityTracker() {
+ return this.tracker;
+ } // Yatopia end
+
// Paper start
// ret true if no collision
public final boolean checkEntityCollision(IBlockData data, Entity source, VoxelShapeCollision voxelshapedcollision,
@@ -536,6 +544,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki
return b(blockposition.getY());
}
+ public static boolean isOutOfBuildLimitVertically(int y){ return b(y); } // Yatopia - OBFHELPER
public static boolean b(int i) {
return i < 0 || i >= 256;
}
diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
2021-04-21 18:12:08 +02:00
index fab55929f72c5784291b3bc87f7717ac24b7806f..20c2c05ab09ac3f2b2924f2b5bc938fdc5cdd32e 100644
2021-04-18 02:13:30 +02:00
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -55,6 +55,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.shapes.VoxelShapes;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeDefaults;
public class Block extends BlockBase implements IMaterial {
@@ -89,6 +90,17 @@ public class Block extends BlockBase implements IMaterial {
return timing;
}
// Paper end
+
+ // Yatopia start - Port lithium
+ public net.minecraft.world.level.pathfinder.PathType getPathNodeType(IBlockData state) {
+ return PathNodeDefaults.getNodeType(state);
+ }
+
+ public net.minecraft.world.level.pathfinder.PathType getNeighborPathNodeType(IBlockData state) {
+ return PathNodeDefaults.getNeighborNodeType(state);
+ }
+ // Yatopia end
+
public Material getMaterial() { return material; } // Purpur - OBFHELPER
@Nullable
private String name;
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBase.java b/src/main/java/net/minecraft/world/level/block/state/BlockBase.java
index b6de70c3630d96d0782a657c0389ce03839d8c43..611066964d11b2da7ab6dd59c6083c5c25c75de7 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBase.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBase.java
@@ -3,6 +3,8 @@ package net.minecraft.world.level.block.state;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.MapCodec;
+import org.apache.commons.lang3.Validate;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -63,6 +65,7 @@ import net.minecraft.world.phys.Vec3D;
import net.minecraft.world.phys.shapes.VoxelShape;
import net.minecraft.world.phys.shapes.VoxelShapeCollision;
import net.minecraft.world.phys.shapes.VoxelShapes;
+import net.minecraft.world.level.pathfinder.PathType;
public abstract class BlockBase {
@@ -349,6 +352,8 @@ public abstract class BlockBase {
public abstract static class BlockData extends IBlockDataHolder<Block, IBlockData> {
+ public net.minecraft.world.level.pathfinder.PathType pathNodeType = PathType.OPEN; // Yatopia - Port lithium
+ public net.minecraft.world.level.pathfinder.PathType pathNodeTypeNeighbor = PathType.OPEN; // Yatopia - Port lithium
private final int b; public final int getEmittedLight() { return this.b; } // Tuinity - OBFHELPER
private final boolean e; public final boolean isTransparentOnSomeFaces() { return this.e; } // Tuinity - OBFHELPER
private final boolean f;
@@ -420,6 +425,16 @@ public abstract class BlockBase {
}
// Tuinity end
+ // Yatopia start - Port lithium
+ public net.minecraft.world.level.pathfinder.PathType getPathNodeType() {
+ return this.pathNodeType;
+ }
+
+ public net.minecraft.world.level.pathfinder.PathType getNeighborPathNodeType() {
+ return this.pathNodeTypeNeighbor;
+ }
+ // Yatopia end
+
public void a() {
this.fluid = this.getBlock().d(this.p()); // Paper - moved from getFluid()
this.isTicking = this.getBlock().isTicking(this.p()); // Paper - moved from isTicking()
@@ -429,6 +444,9 @@ public abstract class BlockBase {
this.shapeExceedsCube = this.a == null || this.a.c; // Tuinity - moved from actual method to here
this.staticPathType = null; // Tuinity - cache static path type
this.neighbourOverridePathType = null; // Tuinity - cache static path types
+ IBlockData state = this.getBlockData(); // Yatopia - Port lithium
+ this.pathNodeType = Validate.notNull(this.getBlock().getPathNodeType(state)); // Yatopia - Port lithium
+ this.pathNodeTypeNeighbor = Validate.notNull(this.getBlock().getNeighborPathNodeType(state)); // Yatopia - Port lithium
this.opacityIfCached = this.a == null || this.isConditionallyFullOpaque() ? -1 : this.a.getOpacity(); // Tuinity - cache opacity for light
// Tuinity start - optimise culling shape cache for light
if (this.a != null && this.a.getCullingShapeCache() != null) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/Chunk.java b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
2021-04-21 23:26:49 +02:00
index 2e8eb0bb8fb4f7ce6b92fe01a81327da30e614ae..34af81b75e7927cccc0d4aea1b80ab677ca31795 100644
2021-04-18 02:13:30 +02:00
--- a/src/main/java/net/minecraft/world/level/chunk/Chunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
@@ -68,6 +68,7 @@ import net.minecraft.world.level.material.FluidTypes;
import net.minecraft.world.phys.AxisAlignedBB;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import net.minecraft.world.entity.EntityLiving; // Yatopia
public class Chunk implements IChunkAccess {
2021-04-21 23:26:49 +02:00
@@ -863,6 +864,11 @@ public class Chunk implements IChunkAccess {
2021-04-18 02:13:30 +02:00
if (!this.entitySlices[i].remove(entity)) { // Tuinity - optimise hard colliding entities // Tuinity - entities by class // Tuinity
return;
}
+ // Yatopia start - Port lithium
+ if (entity instanceof EntityLiving) {
+ this.world.getEntityTracker().onEntityAdded(entity.chunkX, entity.chunkY, entity.chunkZ, (EntityLiving) entity);
+ }
+ // Yatopia end
if (entity instanceof EntityItem) {
itemCounts[i]--;
} else if (entity instanceof IInventory) {
2021-04-21 23:26:49 +02:00
@@ -872,6 +878,11 @@ public class Chunk implements IChunkAccess {
2021-04-18 02:13:30 +02:00
this.markDirty(); // Paper
// Paper end
this.entities.remove(entity); // Paper
+ // Yatopia start - Port lithium
+ if (entity instanceof EntityLiving) {
+ this.world.getEntityTracker().onEntityRemoved(entity.chunkX, entity.chunkY, entity.chunkZ, (EntityLiving) entity);
+ }
+ // Yatopia end
}
public final int getHighestBlockY(HeightMap.Type heightmap_type, int i, int j) { return this.getHighestBlock(heightmap_type, i, j) + 1; } // Paper - sort of an obfhelper, but without -1
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
index b5eb43174d2c2f34bb17bbcdb803aafe58989678..92f6301005031f4afab225e9fd01010eacd3ed26 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
@@ -177,6 +177,7 @@ public class ChunkSection {
return 2 + this.blockIds.c();
}
+ public boolean hasAny(Predicate<IBlockData> predicate) { return a(predicate); } // Yatopia - OBFHELPER
public boolean a(Predicate<IBlockData> predicate) {
return this.blockIds.contains(predicate);
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java
index f70c14385c95763b5f270a6e2ce372cf047ba7bb..5968712468ad1bea4894d31d0d08b213735743e5 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java
@@ -13,10 +13,13 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import java.io.File;
import java.io.IOException;
+import java.util.BitSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
+import java.util.stream.Stream;
import javax.annotation.Nullable;
import net.minecraft.SharedConstants;
import net.minecraft.SystemUtils;
@@ -29,27 +32,118 @@ import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.World;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import me.jellysquid.mods.lithium.common.util.Collector;
+import me.jellysquid.mods.lithium.common.util.collections.ListeningLong2ObjectOpenHashMap;
public class RegionFileSection<R> extends RegionFileCache implements AutoCloseable { // Paper - nuke IOWorker
private static final Logger LOGGER = LogManager.getLogger();
// Paper - nuke IOWorker
- private final Long2ObjectMap<Optional<R>> c = new Long2ObjectOpenHashMap(); protected final Long2ObjectMap<Optional<R>> getDataBySection() { return this.c; } // Tuinity - OBFHELPER
+ private Long2ObjectMap<Optional<R>> c = new Long2ObjectOpenHashMap(); protected final Long2ObjectMap<Optional<R>> getDataBySection() { return this.c; } // Tuinity - OBFHELPER
public final LongLinkedOpenHashSet d = new LongLinkedOpenHashSet(); protected final LongLinkedOpenHashSet getDirtySections() { return this.d; } // Paper - private -> public // Tuinity - OBFHELPER
private final Function<Runnable, Codec<R>> e;
private final Function<Runnable, R> f;
private final DataFixer g;
private final DataFixTypes h;
+ private Long2ObjectOpenHashMap<BitSet> columns; // Yatopia - Port lithium
+
public RegionFileSection(File file, Function<Runnable, Codec<R>> function, Function<Runnable, R> function1, DataFixer datafixer, DataFixTypes datafixtypes, boolean flag) {
super(file, flag); // Paper - nuke IOWorker
this.e = function;
this.f = function1;
this.g = datafixer;
this.h = datafixtypes;
+ // Yatopia Start - Port lithium
+ this.columns = new Long2ObjectOpenHashMap<>();
+ this.c = new ListeningLong2ObjectOpenHashMap<>(this::onEntryAdded, this::onEntryRemoved);
//this.b = new IOWorker(file, flag, file.getName()); // Paper - nuke IOWorker
}
+ private void onEntryRemoved(long key, Optional<R> value) {
+ // NO-OP... vanilla never removes anything, leaking entries.
+ // We might want to fix this.
+ }
+
+ private void onEntryAdded(long key, Optional<R> value) {
+ int y = SectionPosition.unpackY(key);
+
+ // We only care about items belonging to a valid sub-chunk
+ if (y < 0 || y >= 16) {
+ return;
+ }
+
+ int x = SectionPosition.unpackX(key);
+ int z = SectionPosition.unpackZ(key);
+
+ long pos = ChunkCoordIntPair.pair(x, z);
+
+ BitSet flags = this.columns.get(pos);
+
+ if (flags == null) {
+ this.columns.put(pos, flags = new BitSet(16));
+ }
+
+ flags.set(y, value.isPresent());
+ }
+
+ public Stream<R> getWithinChunkColumn(int chunkX, int chunkZ) {
+ BitSet flags = this.getCachedColumnInfo(chunkX, chunkZ);
+
+ // No items are present in this column
+ if (flags.isEmpty()) {
+ return Stream.empty();
+ }
+
+ return flags.stream()
+ .mapToObj((chunkY) -> this.getDataBySection().get(SectionPosition.asLong(chunkX, chunkY, chunkZ)).orElse(null))
+ .filter(Objects::nonNull);
+ }
+
+ public boolean collectWithinChunkColumn(int chunkX, int chunkZ, Collector<R> consumer) {
+ BitSet flags = this.getCachedColumnInfo(chunkX, chunkZ);
+
+ // No items are present in this column
+ if (flags.isEmpty()) {
+ return true;
+ }
+
+ for (int chunkY = flags.nextSetBit(0); chunkY >= 0; chunkY = flags.nextSetBit(chunkY + 1)) {
+ R obj = this.getDataBySection().get(SectionPosition.asLong(chunkX, chunkY, chunkZ)).orElse(null);
+
+ if (obj != null && !consumer.collect(obj)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private BitSet getCachedColumnInfo(int chunkX, int chunkZ) {
+ long pos = ChunkCoordIntPair.pair(chunkX, chunkZ);
+
+ BitSet flags = this.getColumnInfo(pos, false);
+
+ if (flags != null) {
+ return flags;
+ }
+
+ this.loadDataAt(new ChunkCoordIntPair(pos));
+
+ return this.getColumnInfo(pos, true);
+ }
+
+ private BitSet getColumnInfo(long pos, boolean required) {
+ BitSet set = this.columns.get(pos);
+
+ if (set == null && required) {
+ throw new NullPointerException("No data is present for column: " + new ChunkCoordIntPair(pos));
+ }
+
+ return set;
+ }
+ // Yatopia end
+
protected void a(BooleanSupplier booleansupplier) {
while (!this.d.isEmpty() && booleansupplier.getAsBoolean()) {
ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(this.d.firstLong()).r(); // Paper - conflict here to avoid obfhelpers
@@ -92,6 +186,7 @@ public class RegionFileSection<R> extends RegionFileCache implements AutoCloseab
return true;
}
// Tuinity end - actually unload POI data
+ protected Optional<R> get(long i){ return d(i); } // Yatopia - OBFHELPER
@Nullable public final Optional<R> getIfLoaded(long value) { return this.c(value); } // Tuinity - OBFHELPER // Tuinity - OBFHELPER
@Nullable protected Optional<R> c(long i) { // Tuinity - OBFHELPER
@@ -125,6 +220,7 @@ public class RegionFileSection<R> extends RegionFileCache implements AutoCloseab
return World.b(SectionPosition.c(sectionposition.b()));
}
+ protected R getOrCreate(long i){ return e(i); } // Yatopia - OBFHELPER
protected R e(long i) {
Optional<R> optional = this.d(i);
@@ -140,6 +236,7 @@ public class RegionFileSection<R> extends RegionFileCache implements AutoCloseab
}
}
+ public void loadDataAt(ChunkCoordIntPair chunkcoordintpair) { b(chunkcoordintpair); } // Yatopia - OBFHELPER
private void b(ChunkCoordIntPair chunkcoordintpair) {
// Paper start - load data in function
this.loadInData(chunkcoordintpair, this.c(chunkcoordintpair));
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java b/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java
index 76e19f3a4ae988f6f3b59763d639fa5e084fa0bf..be67d9a931aec19105305f316959e97813c4feda 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java
@@ -3,6 +3,8 @@ package net.minecraft.world.level.pathfinder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeCache; // Yatopia
+
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@@ -48,7 +50,7 @@ public class Pathfinder {
@Nullable
private PathEntity a(PathPoint pathpoint, List<Map.Entry<PathDestination, BlockPosition>> list, float f, int i, float f1) { // Paper - optimize collection
//Set<PathDestination> set = map.keySet(); // Paper
-
+ PathNodeCache.enableChunkCache(); // Yatopia - Port lithium
pathpoint.e = 0.0F;
pathpoint.f = this.a(pathpoint, list); // Paper - optimize collection
pathpoint.g = pathpoint.f;
diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java b/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java
index 9d08094165cf18d99116b5c721fff888f3cb42e2..b95804e73050dc7eb9786ca4bb5ea095904bad45 100644
--- a/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java
+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java
@@ -4,6 +4,9 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
+import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeCache; // Yatopia
+import me.jellysquid.mods.lithium.common.world.WorldHelper; // Yatopia
+
import java.util.EnumSet;
import java.util.Iterator;
import javax.annotation.Nullable;
@@ -30,6 +33,10 @@ import net.minecraft.world.level.material.Material;
import net.minecraft.world.phys.AxisAlignedBB;
import net.minecraft.world.phys.Vec3D;
import net.minecraft.world.phys.shapes.VoxelShape;
+import net.minecraft.world.level.chunk.ChunkSection; // Yatopia
+import net.minecraft.world.level.chunk.IChunkAccess; // Yatopia
+import net.minecraft.world.level.World; // Yatopia
+import net.minecraft.world.level.ICollisionAccess; // Yatopia
public class PathfinderNormal extends PathfinderAbstract {
@@ -476,68 +483,111 @@ public class PathfinderNormal extends PathfinderAbstract {
return pathtype;
}
+ /**
+ * @reason Use optimized implementation
+ * @author JellySquid
+ */
public static PathType a(IBlockAccess iblockaccess, BlockPosition.MutableBlockPosition blockposition_mutableblockposition, PathType pathtype) {
- int i = blockposition_mutableblockposition.getX();
- int j = blockposition_mutableblockposition.getY();
- int k = blockposition_mutableblockposition.getZ();
+ // Yatopia start - Port lithium
+ int x = blockposition_mutableblockposition.getX();
+ int y = blockposition_mutableblockposition.getY();
+ int z = blockposition_mutableblockposition.getZ();
+
+ ChunkSection section = null;
+
+ // Check that all the block's neighbors are within the same chunk column. If so, we can isolate all our block
+ // reads to just one chunk and avoid hits against the server chunk manager.
+ if (iblockaccess instanceof ICollisionAccess && WorldHelper.areNeighborsWithinSameChunk(blockposition_mutableblockposition)) {
+ // If the y-coordinate is within bounds, we can cache the chunk section. Otherwise, the if statement to check
+ // if the cached chunk section was initialized will early-exit.
+ if (!World.b(y)) {
+ // This cast is always safe and is necessary to obtain direct references to chunk sections.
+ IChunkAccess chunk = (IChunkAccess) ((ICollisionAccess) iblockaccess).c(x >> 4, z >> 4);
+
+ // If the chunk is absent, the cached section above will remain null, as there is no chunk section anyways.
+ // An empty chunk or section will never pose any danger sources, which will be caught later.
+ if (chunk != null) {
+ section = chunk.getSections()[y >> 4];
+ }
+ }
- for (int l = -1; l <= 1; ++l) {
- for (int i1 = -1; i1 <= 1; ++i1) {
- for (int j1 = -1; j1 <= 1; ++j1) {
- if (l != 0 || j1 != 0) {
- blockposition_mutableblockposition.d(i + l, j + i1, k + j1);
- // Paper start
- IBlockData iblockdata = iblockaccess.getTypeIfLoaded(blockposition_mutableblockposition);
- if (iblockdata == null) {
- pathtype = PathType.BLOCKED;
- } else {
- // Paper end
- // Tuinity start - reduce pathfinder branching
- if (iblockdata.neighbourOverridePathType == PathType.OPEN) {
- continue;
- } else if (iblockdata.neighbourOverridePathType != null) {
- return iblockdata.neighbourOverridePathType;
- }
- // Tuinity end - reduce pathfinder branching
- if (iblockdata.a(Blocks.CACTUS)) {
- return iblockdata.neighbourOverridePathType = PathType.DANGER_CACTUS; // Tuinity - reduce pathfinder branching
- }
+ // If we can guarantee that blocks won't be modified while the cache is active, try to see if the chunk
+ // section is empty or contains any dangerous blocks within the palette. If not, we can assume any checks
+ // against this chunk section will always fail, allowing us to fast-exit.
+ if (ChunkSection.isEmpty(section) || PathNodeCache.isSectionSafeAsNeighbor(section)) {
+ return pathtype;
+ }
+ }
- if (iblockdata.a(Blocks.SWEET_BERRY_BUSH) || iblockdata.a(Blocks.STONECUTTER)) { // Purpur
- return iblockdata.neighbourOverridePathType = PathType.DANGER_OTHER; // Tuinity - reduce pathfinder branching
- }
+ int xStart = x - 1;
+ int yStart = y - 1;
+ int zStart = z - 1;
+
+ int xEnd = x + 1;
+ int yEnd = y + 1;
+ int zEnd = z + 1;
+
+ // Vanilla iteration order is XYZ
+ for (int adjX = xStart; adjX <= xEnd; adjX++) {
+ for (int adjY = yStart; adjY <= yEnd; adjY++) {
+ for (int adjZ = zStart; adjZ <= zEnd; adjZ++) {
+ // Skip the vertical column of the origin block
+ if (adjX == x && adjZ == z) {
+ continue;
+ }
- if (a(iblockdata)) {
- return iblockdata.neighbourOverridePathType = PathType.DANGER_FIRE; // Tuinity - reduce pathfinder branching
- }
+ IBlockData state;
- if (iblockdata.getFluid().a((Tag) TagsFluid.WATER)) { // Paper - remove another getType call
- return iblockdata.neighbourOverridePathType = PathType.WATER_BORDER; // Tuinity - reduce pathfinder branching
- }
- iblockdata.neighbourOverridePathType = PathType.OPEN; // Tuinity - reduce pathfinder branching
- } // Paper
+ // If we're not accessing blocks outside a given section, we can greatly accelerate block state
+ // retrieval by calling upon the cached chunk directly.
+ if (section != null) {
+ state = section.getType(adjX & 15, adjY & 15, adjZ & 15);
+ } else {
+ state = iblockaccess.getType(blockposition_mutableblockposition.setValues(adjX, adjY, adjZ));
+ }
+
+ // Ensure that the block isn't air first to avoid expensive hash table accesses
+ if (state.isAir()) {
+ continue;
+ }
+
+ PathType neighborType = PathNodeCache.getNeighborPathNodeType(state);
+
+ if (neighborType != PathType.OPEN) {
+ return neighborType;
}
}
}
}
- return pathtype;
+ return pathtype; // Yatopia end
}
+ /**
+ * @reason Use optimized implementation which avoids scanning blocks for dangers where possible
+ * @author JellySquid
+ */
protected static PathType b(IBlockAccess iblockaccess, BlockPosition blockposition) {
- IBlockData iblockdata = iblockaccess.getTypeIfLoaded(blockposition); // Paper
- if (iblockdata == null) return PathType.BLOCKED; // Paper
- // Tuinity start - reduce pathfinder branches
- if (iblockdata.staticPathType != null) {
- return iblockdata.staticPathType;
- }
- if (iblockdata.getShapeCache() == null) {
- // while it might be called static, it might vary on shape! However, only a few blocks have variable shape.
- // So we rarely enter here.
- return getStaticTypeSlow(iblockaccess, blockposition, iblockdata);
- } else {
- return iblockdata.staticPathType = getStaticTypeSlow(iblockaccess, blockposition, iblockdata);
+ // Yatopia start - Port lithium
+ IBlockData blockState = iblockaccess.getType(blockposition);
+ if (blockState == null) return PathType.BLOCKED;
+ PathType type = PathNodeCache.getPathNodeType(blockState);
+
+ // If the node type is open, it means that we were unable to determine a more specific type, so we need
+ // to check the fallback path.
+ if (type == PathType.OPEN) {
+ // This is only ever called in vanilla after all other possibilities are exhausted, but before fluid checks
+ // It should be safe to perform it last in actuality and take advantage of the cache for fluid types as well
+ // since fluids will always pass this check.
+ if (!blockState.a(iblockaccess, blockposition, PathMode.LAND)) {
+ return PathType.BLOCKED;
+ }
+
+ // All checks succeed, this path node really is open!
+ return PathType.OPEN;
}
+ return type;
+ // Yatopia end
}
protected static PathType getStaticTypeSlow(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
// Tuinity end - reduce pathfinder branches
diff --git a/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java b/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java
index 292821f039d99a03ba4daeb3a941616ef5f6287e..9b7110a805b81906512acc3771217a56a102db67 100644
--- a/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java
+++ b/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java
@@ -42,7 +42,7 @@ public class ControllerLookWASD extends ControllerLook {
entity.setHeadRotation(entity.yaw);
entity.pitch = normalizePitch(pitch + pitchOffset);
- entity.tracker.broadcast(new PacketPlayOutEntity
+ entity.getTracker().broadcast(new PacketPlayOutEntity // Yatopia
.PacketPlayOutRelEntityMoveLook(entity.getId(),
(short) 0, (short) 0, (short) 0,
(byte) MathHelper.d(entity.yaw * 256.0F / 360.0F),