diff --git a/gradle.properties b/gradle.properties
index 569e5ff..1da6569 100755
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
group=net.gensokyoreimagined.nitori
-version=1.0.5-SNAPSHOT
+version=1.0.6-SNAPSHOT
description=Converting patches into mixins, for the Ignite Framework
org.gradle.parallel=true
diff --git a/src/main/java/net/gensokyoreimagined/nitori/cached_blockpos_iteration/IterateOutwardsCache.java b/src/main/java/net/gensokyoreimagined/nitori/cached_blockpos_iteration/IterateOutwardsCache.java
new file mode 100644
index 0000000..827a1b5
--- /dev/null
+++ b/src/main/java/net/gensokyoreimagined/nitori/cached_blockpos_iteration/IterateOutwardsCache.java
@@ -0,0 +1,91 @@
+// Nitori Copyright (C) 2024 Gensokyo Reimagined
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+package net.gensokyoreimagined.nitori.cached_blockpos_iteration;
+
+/*
+ * Originally from CaffeineMC, licensed under GNU Lesser General Public License v3.0
+ * See https://github.com/CaffeineMC/lithium-fabric for more information/sources
+ */
+
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.longs.LongList;
+import net.minecraft.core.BlockPos;
+
+import java.util.Iterator;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author 2No2Name, original implemenation by SuperCoder7979 and Gegy1000
+ */
+public class IterateOutwardsCache {
+ //POS_ZERO must not be replaced with BlockPos.ORIGIN, otherwise iterateOutwards at BlockPos.ORIGIN will not use the cache
+ public static final BlockPos POS_ZERO = new BlockPos(0,0,0);
+
+
+ private final ConcurrentHashMap table;
+ private final int capacity;
+ private final Random random;
+
+ public IterateOutwardsCache(int capacity) {
+ this.capacity = capacity;
+ this.table = new ConcurrentHashMap<>(31);
+ this.random = new Random();
+ }
+
+ private void fillPositionsWithIterateOutwards(LongList entry, int xRange, int yRange, int zRange) {
+ // Add all positions to the cached list
+ for (BlockPos pos : BlockPos.withinManhattan(POS_ZERO, xRange, yRange, zRange)) {
+ entry.add(pos.asLong());
+ }
+ }
+
+ public LongList getOrCompute(int xRange, int yRange, int zRange) {
+ long key = BlockPos.asLong(xRange, yRange, zRange);
+
+ LongArrayList entry = this.table.get(key);
+ if (entry != null) {
+ return entry;
+ }
+
+ // Cache miss: compute and store
+ entry = new LongArrayList(128);
+
+ this.fillPositionsWithIterateOutwards(entry, xRange, yRange, zRange);
+
+ //decrease the array size, as of now it won't be modified anymore anyways
+ entry.trim();
+
+ //this might overwrite an entry as the same entry could have been computed and added during this thread's computation
+ //we do not use computeIfAbsent, as it can delay other threads for too long
+ Object previousEntry = this.table.put(key, entry);
+
+
+ if (previousEntry == null && this.table.size() > this.capacity) {
+ //prevent a memory leak by randomly removing about 1/8th of the elements when the exceed the desired capacity is exceeded
+ final Iterator iterator = this.table.keySet().iterator();
+ //prevent an unlikely infinite loop caused by another thread filling the table concurrently using counting
+ for (int i = -this.capacity; iterator.hasNext() && i < 5; i++) {
+ Long key2 = iterator.next();
+ //random is not threadsafe, but it doesn't matter here, because we don't need quality random numbers
+ if (this.random.nextInt(8) == 0 && key2 != key) {
+ iterator.remove();
+ }
+ }
+ }
+
+ return entry;
+ }
+}
diff --git a/src/main/java/net/gensokyoreimagined/nitori/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java b/src/main/java/net/gensokyoreimagined/nitori/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java
new file mode 100644
index 0000000..cb15ac1
--- /dev/null
+++ b/src/main/java/net/gensokyoreimagined/nitori/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java
@@ -0,0 +1,66 @@
+// Nitori Copyright (C) 2024 Gensokyo Reimagined
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+package net.gensokyoreimagined.nitori.cached_blockpos_iteration;
+
+/*
+ * Originally from CaffeineMC, licensed under GNU Lesser General Public License v3.0
+ * See https://github.com/CaffeineMC/lithium-fabric for more information/sources
+ */
+
+import it.unimi.dsi.fastutil.longs.LongIterator;
+import it.unimi.dsi.fastutil.longs.LongList;
+import net.minecraft.core.BlockPos;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Iterator;
+
+/**
+ * @author 2No2Name
+ */
+public class LongList2BlockPosMutableIterable implements Iterable {
+
+ private final LongList positions;
+ private final int xOffset, yOffset, zOffset;
+
+ public LongList2BlockPosMutableIterable(BlockPos offset, LongList posList) {
+ this.xOffset = offset.getX();
+ this.yOffset = offset.getY();
+ this.zOffset = offset.getZ();
+ this.positions = posList;
+ }
+
+ @Override
+ public @NotNull Iterator iterator() {
+ return new Iterator<>() {
+
+ private final LongIterator it = LongList2BlockPosMutableIterable.this.positions.iterator();
+ private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
+
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public net.minecraft.core.BlockPos next() {
+ long nextPos = this.it.nextLong();
+ return this.pos.set(
+ LongList2BlockPosMutableIterable.this.xOffset + BlockPos.getX(nextPos),
+ LongList2BlockPosMutableIterable.this.yOffset + BlockPos.getY(nextPos),
+ LongList2BlockPosMutableIterable.this.zOffset + BlockPos.getZ(nextPos));
+ }
+ };
+ }
+}
diff --git a/src/main/java/net/gensokyoreimagined/nitori/core/MixinBlockPos.java b/src/main/java/net/gensokyoreimagined/nitori/core/MixinBlockPos.java
new file mode 100644
index 0000000..3de70bf
--- /dev/null
+++ b/src/main/java/net/gensokyoreimagined/nitori/core/MixinBlockPos.java
@@ -0,0 +1,44 @@
+// Nitori Copyright (C) 2024 Gensokyo Reimagined
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+package net.gensokyoreimagined.nitori.core;
+
+import it.unimi.dsi.fastutil.longs.LongList;
+import net.gensokyoreimagined.nitori.cached_blockpos_iteration.IterateOutwardsCache;
+import net.gensokyoreimagined.nitori.cached_blockpos_iteration.LongList2BlockPosMutableIterable;
+import net.minecraft.core.BlockPos;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import static net.gensokyoreimagined.nitori.cached_blockpos_iteration.IterateOutwardsCache.POS_ZERO;
+
+@Mixin(BlockPos.class)
+public class MixinBlockPos {
+ // Implementation of 0068-lithium-cache-iterate-outwards.patch
+ private static final IterateOutwardsCache ITERATE_OUTWARDS_CACHE = new IterateOutwardsCache(50);
+ // Implementation of 0068-lithium-cache-iterate-outwards.patch
+ private static final LongList HOGLIN_PIGLIN_CACHE = ITERATE_OUTWARDS_CACHE.getOrCompute(8, 4, 8);
+
+ // Implementation of 0068-lithium-cache-iterate-outwards.patch
+ @Inject(method = "withinManhattan", at = @At("HEAD"), cancellable = true)
+ private static void withinManhattan(BlockPos center, int rangeX, int rangeY, int rangeZ, CallbackInfoReturnable> cir) {
+ if (center != POS_ZERO) {
+ final LongList positions = rangeX == 8 && rangeY == 4 && rangeZ == 8 ? HOGLIN_PIGLIN_CACHE : ITERATE_OUTWARDS_CACHE.getOrCompute(rangeX, rangeY, rangeZ);
+ cir.setReturnValue(new LongList2BlockPosMutableIterable(center, positions));
+ cir.cancel();
+ }
+ }
+}
diff --git a/src/main/resources/ignite.mod.json b/src/main/resources/ignite.mod.json
index 28284c7..096f094 100755
--- a/src/main/resources/ignite.mod.json
+++ b/src/main/resources/ignite.mod.json
@@ -1,6 +1,6 @@
{
"id": "Nitori",
- "version": "1.0.5",
+ "version": "1.0.6",
"mixins": [
"mixins.core.json"
]
diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json
index 7c498fc..bbbee7f 100755
--- a/src/main/resources/mixins.core.json
+++ b/src/main/resources/mixins.core.json
@@ -19,6 +19,7 @@
"MixinEntity",
"MixinMob",
"MixinWorldGenRegion",
- "MixinNoiseBasedChunkGenerator"
+ "MixinNoiseBasedChunkGenerator",
+ "MixinBlockPos"
]
}