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" ] }