diff --git a/README.md b/README.md index de8e381..f3d4c24 100755 --- a/README.md +++ b/README.md @@ -1,34 +1,39 @@ # Nitori A performance mod that converts patches into mixins using the Ignite Framework for Paper/Spigot. -## Optimizations +## Optimizations: This plugin provides the following optimizations: -- [x] Iterate entity trackers faster by using Int2ObjectLinkedOpenHashMap -- [ ] Reduce constants allocations -- [x] Entity Micro Optimizations -- [ ] Lithium mixins - - [x] Fast util - - [x] HashedReferenceList - - [x] CompactSineLUT - - [x] Fast retrieval - - [x] Cached hashcode - - [x] Store gamerules in fastutil hashmap - - [ ] Precompute shape arrays - - [ ] Collections.attributes - - [ ] Collections.entity_by_type - - [ ] Collections.entity_filtering - - [ ] Chunk serialization - - [x] Cache iterate outwards - - [ ] Block moving block shapes - - [ ] Shapes blockstate cache - - [x] Lithium gen - - [ ] Ai sensor secondary POI - - [ ] World tick scheduler -- [x] Smarter statistics-ticking -- [ ] Async Pathfinding -- [x] Multithreaded Tracker +- Faster Entity tracker by utilizing **Multiple Cores** this will allow larger servers to have way more entities +- Async NBT data saving which improves where paper doesn't on world saving +- Many of Lithium's Optimization patches which includes: + - Faster Math + - Faster Entity retrieval + - Reduced Memory allocations + - Improved inlined logics + - Improved collections + - Pre-Computed Shape arrays + - Improved Block Entity tickings + - Lithium Chunk Gen + - Mob Ai Improvements (soon) + - Fast BlockPos + - Faster entity related inmterractions (Hand swing, Sprinting particles etc.) + - ...and much more +- Includes some of the patches from Very Many Players (VMP)'s + - Improved player tracking logic + - Improved TypeFilterableList + - If entity velocity is zero it won't send it as packets + - Improved Player lookups + - Faster VarInts +- Some patches from Potatoptimize + - Way faster math + - Faster rotation logic + - Many Inlined logics -**NOTE: This mod may or may not alter the default behaviors of some mob AI.** +## Optimizations To-Do: +- Async Mob Pathfinding +- Multithreading starlight using ScaleableLux +- Easier config to toggle optimizations on and off +- Improving EntityTickList further --- diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/math/random/RandomGeneratorRandom.java b/src/main/java/net/gensokyoreimagined/nitori/common/math/random/RandomGeneratorRandom.java new file mode 100644 index 0000000..3123612 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/math/random/RandomGeneratorRandom.java @@ -0,0 +1,111 @@ +package net.gensokyoreimagined.nitori.common.math.random; + +import com.google.common.annotations.VisibleForTesting; +import net.minecraft.util.Mth; +import net.minecraft.world.level.levelgen.BitRandomSource; +import net.minecraft.world.level.levelgen.LegacyRandomSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.PositionalRandomFactory; + +import java.util.random.RandomGenerator; +import java.util.random.RandomGeneratorFactory; + +public class RandomGeneratorRandom implements BitRandomSource { + private static final RandomGeneratorFactory RANDOM_GENERATOR_FACTORY = RandomGeneratorFactory.of("L64X128MixRandom"); + + private static final int INT_BITS = 48; + private static final long SEED_MASK = 0xFFFFFFFFFFFFL; + private static final long MULTIPLIER = 25214903917L; + private static final long INCREMENT = 11L; + + private long seed; + private RandomGenerator.SplittableGenerator randomGenerator; + + public RandomGeneratorRandom(long seed) { + this.seed = seed; + this.randomGenerator = RANDOM_GENERATOR_FACTORY.create(seed); + } + + @Override + public RandomSource fork() { + return new RandomGeneratorRandom(this.nextLong()); + } + + @Override + public PositionalRandomFactory forkPositional() { + return new Splitter(this.nextLong()); + } + + @Override + public void setSeed(long seed) { + this.seed = seed; + this.randomGenerator = RANDOM_GENERATOR_FACTORY.create(seed); + } + + @Override + public int next(int bits) { + // >>> instead of Mojang's >> fixes MC-239059 + return (int) ((seed * MULTIPLIER + INCREMENT & SEED_MASK) >>> INT_BITS - bits); + } + + @Override + public int nextInt() { + return randomGenerator.nextInt(); + } + + @Override + public int nextInt(int bound) { + return randomGenerator.nextInt(bound); + } + + @Override + public long nextLong() { + return randomGenerator.nextLong(); + } + + @Override + public boolean nextBoolean() { + return randomGenerator.nextBoolean(); + } + + @Override + public float nextFloat() { + return randomGenerator.nextFloat(); + } + + @Override + public double nextDouble() { + return randomGenerator.nextDouble(); + } + + @Override + public double nextGaussian() { + return randomGenerator.nextGaussian(); + } + + private record Splitter(long seed) implements PositionalRandomFactory { + @Override + public RandomSource at(int x, int y, int z) { + return new RandomGeneratorRandom(Mth.getSeed(x, y, z) ^ this.seed); + } + + @Override + public RandomSource fromHashOf(String seed) { + return new RandomGeneratorRandom((long) seed.hashCode() ^ this.seed); + } + + /* + @Override + public Random split(long seed) { + return new RandomGeneratorRandom(seed); + } + + */ + + @Override + @VisibleForTesting + public void parityConfigString(StringBuilder info) { + info.append("RandomGeneratorRandom$Splitter{").append(this.seed).append("}"); + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/scheduler/OrderedTickQueue.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/scheduler/OrderedTickQueue.java new file mode 100644 index 0000000..f232af8 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/scheduler/OrderedTickQueue.java @@ -0,0 +1,192 @@ +package net.gensokyoreimagined.nitori.common.world.scheduler; + +// ChunkTickSchedulerMixin needs this + + + +//import it.unimi.dsi.fastutil.HashCommon; +//import net.minecraft.world.ticks.ScheduledTick; +// +//import java.util.*; +// +//public class OrderedTickQueue extends AbstractQueue> { +// private static final int INITIAL_CAPACITY = 16; +// private static final Comparator> COMPARATOR = Comparator.comparingLong(ScheduledTick::subTickOrder); +// +// private ScheduledTick[] arr; +// +// private int lastIndexExclusive; +// private int firstIndex; +// +// private long currentMaxSubTickOrder = Long.MIN_VALUE; +// private boolean isSorted; +// private ScheduledTick unsortedPeekResult; +// +// @SuppressWarnings("unchecked") +// public OrderedTickQueue(int capacity) { +// this.arr = (ScheduledTick[]) new ScheduledTick[capacity]; +// this.lastIndexExclusive = 0; +// this.isSorted = true; +// this.unsortedPeekResult = null; +// this.firstIndex = 0; +// } +// +// public OrderedTickQueue() { +// this(INITIAL_CAPACITY); +// } +// +// @Override +// public void clear() { +// Arrays.fill(this.arr, null); +// this.lastIndexExclusive = 0; +// this.firstIndex = 0; +// this.currentMaxSubTickOrder = Long.MIN_VALUE; +// this.isSorted = true; +// this.unsortedPeekResult = null; +// } +// +// @Override +// public Iterator> iterator() { +// if (this.isEmpty()) { +// return Collections.emptyIterator(); +// } +// this.sort(); +// return new Iterator<>() { +// int nextIndex = OrderedTickQueue.this.firstIndex; +// +// @Override +// public boolean hasNext() { +// return this.nextIndex < OrderedTickQueue.this.lastIndexExclusive; +// } +// +// @Override +// public ScheduledTick next() { +// return OrderedTickQueue.this.arr[this.nextIndex++]; +// } +// }; +// } +// +// @Override +// public ScheduledTick poll() { +// if (this.isEmpty()) { +// return null; +// } +// if (!this.isSorted) { +// this.sort(); +// } +// ScheduledTick nextTick; +// int polledIndex = this.firstIndex++; +// ScheduledTick[] ticks = this.arr; +// nextTick = ticks[polledIndex]; +// ticks[polledIndex] = null; +// return nextTick; +// } +// +// @Override +// public ScheduledTick peek() { +// if (!this.isSorted) { +// return this.unsortedPeekResult; +// } else if (this.lastIndexExclusive > this.firstIndex) { +// return this.getTickAtIndex(this.firstIndex); +// } +// return null; +// } +// +// public boolean offer(ScheduledTick tick) { +// if (this.lastIndexExclusive >= this.arr.length) { +// //todo remove consumed elements first +// this.arr = copyArray(this.arr, HashCommon.nextPowerOfTwo(this.arr.length + 1)); +// } +// if (tick.subTickOrder() <= this.currentMaxSubTickOrder) { +// //Set to unsorted instead of slowing down the insertion +// //This is rare but may happen in bulk +// //Sorting later needs O(n*log(n)) time, but it only needs to happen when unordered insertion needs to happen +// //Therefore it is better than n times log(n) time of the PriorityQueue that happens on ordered insertion too +// ScheduledTick firstTick = this.isSorted ? this.size() > 0 ? this.arr[this.firstIndex] : null : this.unsortedPeekResult; +// this.isSorted = false; +// this.unsortedPeekResult = firstTick == null || tick.subTickOrder() < firstTick.subTickOrder() ? tick : firstTick; +// } else { +// this.currentMaxSubTickOrder = tick.subTickOrder(); +// } +// this.arr[this.lastIndexExclusive++] = tick; +// return true; +// } +// +// public int size() { +// return this.lastIndexExclusive - this.firstIndex; +// } +// +// private void handleCompaction(int size) { +// // Only compact the array if it is less than 50% filled +// if (this.arr.length > INITIAL_CAPACITY && size < this.arr.length / 2) { +// this.arr = copyArray(this.arr, size); +// } else { +// // Fill the unused array elements with nulls to release our references to the elements in it +// Arrays.fill(this.arr, size, this.arr.length, null); +// } +// +// this.firstIndex = 0; +// this.lastIndexExclusive = size; +// +// if (size == 0 || !this.isSorted) { +// this.currentMaxSubTickOrder = Long.MIN_VALUE; +// } else { +// ScheduledTick tick = this.arr[size - 1]; +// this.currentMaxSubTickOrder = tick == null ? Long.MIN_VALUE : tick.subTickOrder(); +// } +// } +// +// public void sort() { +// if (this.isSorted) { +// return; +// } +// this.removeNullsAndConsumed(); +// Arrays.sort(this.arr, this.firstIndex, this.lastIndexExclusive, COMPARATOR); +// this.isSorted = true; +// this.unsortedPeekResult = null; +// } +// +// public void removeNullsAndConsumed() { +// int src = this.firstIndex; +// int dst = 0; +// while (src < this.lastIndexExclusive) { +// ScheduledTick ScheduledTick = this.arr[src]; +// if (ScheduledTick != null) { +// this.arr[dst] = ScheduledTick; +// dst++; +// } +// src++; +// } +// this.handleCompaction(dst); +// } +// +// public ScheduledTick getTickAtIndex(int index) { +// if (!this.isSorted) { +// throw new IllegalStateException("Unexpected access on unsorted queue!"); +// } +// return this.arr[index]; +// } +// +// public void setTickAtIndex(int index, ScheduledTick tick) { +// if (!this.isSorted) { +// throw new IllegalStateException("Unexpected access on unsorted queue!"); +// } +// this.arr[index] = tick; +// } +// +// @SuppressWarnings("unchecked") +// private static ScheduledTick[] copyArray(ScheduledTick[] src, int size) { +// final ScheduledTick[] copy = new ScheduledTick[Math.max(INITIAL_CAPACITY, size)]; +// +// if (size != 0) { +// System.arraycopy(src, 0, copy, 0, Math.min(src.length, size)); +// } +// +// return copy; +// } +// +// @Override +// public boolean isEmpty() { +// return this.lastIndexExclusive <= this.firstIndex; +// } +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/biome_temprature_leak/Biome_threadLocalMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/biome_temprature_leak/Biome_threadLocalMixin.java new file mode 100644 index 0000000..0ed64ad --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/biome_temprature_leak/Biome_threadLocalMixin.java @@ -0,0 +1,39 @@ +package net.gensokyoreimagined.nitori.mixin.alloc.biome_temprature_leak; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import it.unimi.dsi.fastutil.longs.Long2FloatLinkedOpenHashMap; +import net.minecraft.world.level.biome.Biome; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.function.Supplier; + +@Mixin(Biome.class) +public abstract class Biome_threadLocalMixin { + + /* + * Not only do I absolutely hate this garbage cache, cause it barely works at all. + * HOW did nobody at mojang notice that this thread local was not static, how on earth is a non-static private + * value suppose to run on multiple threads at the same time. + * So now its static so that it actually works like its suppose to, and you don't need to declare a billion of them + * + * This is a significant performance boost & reduces a lot of memory usage + */ + + private static ThreadLocal memoryLeakFix$betterTempCache; + + @WrapOperation( + method = "", + at = @At( + value = "INVOKE", + target = "Ljava/lang/ThreadLocal;withInitial(Ljava/util/function/Supplier;)Ljava/lang/ThreadLocal;" + ) + ) + private ThreadLocal memoryLeakFix$useStaticThreadLocal(Supplier supplier, Operation> original) { + if (memoryLeakFix$betterTempCache == null) { + memoryLeakFix$betterTempCache = original.call(supplier); + } + return memoryLeakFix$betterTempCache; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/package-info.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/package-info.java new file mode 100644 index 0000000..079801d --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/package-info.java @@ -0,0 +1,6 @@ +package net.gensokyoreimagined.nitori.mixin.alloc; + +/* + /nbt/NbtCompoundMixin is not needed + + */ \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/brain/BrainMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java similarity index 68% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/brain/BrainMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java index f650d18..4d18cb9 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/brain/BrainMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java @@ -1,9 +1,14 @@ -package net.gensokyoreimagined.nitori.mixin.needs_testing.brain; +package net.gensokyoreimagined.nitori.mixin.collections.brain; import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import net.minecraft.world.entity.ai.Brain; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; +import net.minecraft.world.entity.schedule.Activity; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; @@ -14,6 +19,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Collection; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; @Mixin(Brain.class) @@ -29,6 +35,11 @@ public class BrainMixin { @Final private Map sensors; + @Shadow + @Final + @Mutable + private Map, MemoryStatus>>> activityRequirements; + @Inject( method = "", at = @At("RETURN") @@ -36,6 +47,7 @@ public class BrainMixin { private void reinitializeBrainCollections(Collection memories, Collection sensors, ImmutableList memoryEntries, Supplier codecSupplier, CallbackInfo ci) { this.memories = new Reference2ReferenceOpenHashMap<>(this.memories); this.sensors = new Reference2ReferenceLinkedOpenHashMap<>(this.sensors); + this.activityRequirements = new Object2ObjectOpenHashMap<>(this.activityRequirements); } } \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/fluid_submersion/EntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/fluid_submersion/EntityMixin.java new file mode 100644 index 0000000..9d40a71 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/fluid_submersion/EntityMixin.java @@ -0,0 +1,44 @@ +package net.gensokyoreimagined.nitori.mixin.collections.fluid_submersion; + +import it.unimi.dsi.fastutil.objects.ReferenceArraySet; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.tags.TagKey; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Set; + +@Mixin(Entity.class) +public class EntityMixin { + + @Mutable + @Shadow + @Final + private Set> fluidOnEyes; + + @Inject( + method = "", + at = @At("RETURN") + ) + private void useReferenceArraySet(CallbackInfo ci) { + this.fluidOnEyes = new ReferenceArraySet<>(this.fluidOnEyes); + } + + @Redirect( + method = "updateFluidOnEyes", + at = @At(value = "INVOKE", target = "Ljava/util/Set;clear()V"), + require = 0 + ) + private void clearIfNotEmpty(Set set) { + if (!set.isEmpty()) { + set.clear(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/collisions/unpushable_cramming/BoatEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/collisions/unpushable_cramming/BoatEntityMixin.java new file mode 100644 index 0000000..4c8988c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/collisions/unpushable_cramming/BoatEntityMixin.java @@ -0,0 +1,43 @@ +package net.gensokyoreimagined.nitori.mixin.entity.collisions.unpushable_cramming; + +import com.google.common.base.Predicates; +import net.gensokyoreimagined.nitori.common.entity.pushable.EntityPushablePredicate; +import net.gensokyoreimagined.nitori.common.world.WorldHelper; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.vehicle.Boat; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.entity.EntitySectionStorage; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +@Mixin(Boat.class) +public class BoatEntityMixin { + @Redirect( + method = "tick", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;" + ) + ) + private List getOtherPushableEntities(Level world, @Nullable Entity except, AABB box, Predicate predicate) { + //noinspection Guava + if (predicate == Predicates.alwaysFalse()) { + return Collections.emptyList(); + } + if (predicate instanceof EntityPushablePredicate entityPushablePredicate) { + EntitySectionStorage cache = WorldHelper.getEntityCacheOrNull(world); + if (cache != null) { + //noinspection unchecked + return WorldHelper.getPushableEntities(world, cache, except, box, (EntityPushablePredicate) entityPushablePredicate); + } + } + return world.getEntities(except, box, predicate); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/navigation/CancelNavigationMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/navigation/CancelNavigationMixin.java new file mode 100644 index 0000000..bf02a40 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/navigation/CancelNavigationMixin.java @@ -0,0 +1,35 @@ +package net.gensokyoreimagined.nitori.mixin.entity.navigation; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.ai.navigation.PathNavigation; +import net.minecraft.world.level.pathfinder.Path; +import net.minecraft.world.entity.Mob; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Set; + +@Mixin(PathNavigation.class) +public class CancelNavigationMixin { + @Shadow @Final protected Mob mob; + + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) + private void cancelTick(CallbackInfo ci) { + if (mob.isPassenger()) { + ci.cancel(); + } + } + + @Inject(method = "createPath(Ljava/util/Set;Lnet/minecraft/world/entity/Entity;IZIF)Lnet/minecraft/world/level/pathfinder/Path;", at = @At(value = "INVOKE", target = "Ljava/util/Set;isEmpty()Z", shift = At.Shift.AFTER), cancellable = true) + private void cancelFindPath(Set positions, Entity target, int range, boolean useHeadPos, int distance, float followRange, CallbackInfoReturnable cir) { + if (mob.isPassenger()) { + cir.setReturnValue(null); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/speed/EntitySpeedMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/speed/EntitySpeedMixin.java new file mode 100644 index 0000000..bd26235 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/speed/EntitySpeedMixin.java @@ -0,0 +1,25 @@ +package net.gensokyoreimagined.nitori.mixin.entity.speed; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +// Credit Mirai patch #0046 +@Mixin(Entity.class) +public abstract class EntitySpeedMixin { + @Shadow public abstract Vec3 getDeltaMovement(); + + @Shadow protected abstract float getBlockSpeedFactor(); + + @Redirect(method = "move", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;getBlockSpeedFactor()F")) + private float redirectMultiplier(Entity instance) { + if (this.getDeltaMovement().x == 0 && this.getDeltaMovement().z == 0) { + return 1; + } else { + return this.getBlockSpeedFactor(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/random/RandomMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/random/RandomMixin.java new file mode 100644 index 0000000..f1570fa --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/random/RandomMixin.java @@ -0,0 +1,29 @@ +package net.gensokyoreimagined.nitori.mixin.math.random; + +import net.gensokyoreimagined.nitori.common.math.random.RandomGeneratorRandom; +import io.netty.util.internal.ThreadLocalRandom; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.RandomSupport; +import org.jetbrains.annotations.NotNull; +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; + +@Mixin(RandomSource.class) +public interface RandomMixin { + @Inject(method = "create(J)Lnet/minecraft/util/RandomSource;", at = @At(value = "HEAD"), cancellable = true) + private static void fasterrandom$createInject(long seed, @NotNull CallbackInfoReturnable cir) { + cir.setReturnValue(new RandomGeneratorRandom(seed)); + } + + @Inject(method = "createNewThreadLocalInstance", at = @At(value = "HEAD"), cancellable = true) + private static void fasterrandom$createLocalInject(@NotNull CallbackInfoReturnable cir) { + cir.setReturnValue(new RandomGeneratorRandom(ThreadLocalRandom.current().nextLong())); + } + + @Inject(method = "createThreadSafe", at = @At(value = "HEAD"), cancellable = true) + private static void fasterrandom$createThreadSafeInject(@NotNull CallbackInfoReturnable cir) { + cir.setReturnValue(new RandomGeneratorRandom(RandomSupport.generateUniqueSeed())); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/alloc/deep_passengers/EntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/alloc/deep_passengers/EntityMixin.java new file mode 100644 index 0000000..65c6794 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/alloc/deep_passengers/EntityMixin.java @@ -0,0 +1,99 @@ +package net.gensokyoreimagined.nitori.mixin.needs_testing.alloc.deep_passengers; + +import com.google.common.collect.ImmutableList; +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.stream.Stream; + +@Mixin(Entity.class) +public abstract class EntityMixin { + + @Shadow + private ImmutableList passengers; + + /** + * @author 2No2Name + * @reason Avoid stream code + */ + @Overwrite + public Iterable getIndirectPassengers() { + if (this.passengers.isEmpty()) { + return Collections.emptyList(); + } + ArrayList passengers = new ArrayList<>(); + this.addPassengersDeep(passengers); + + return passengers; + } + + /** + * @author 2No2Name + * @reason Avoid stream allocations + */ + @Overwrite + private Stream getIndirectPassengersStream() { + if (this.passengers.isEmpty()) { + return Stream.empty(); + } + ArrayList passengers = new ArrayList<>(); + this.addPassengersDeep(passengers); + + return passengers.stream(); + } + + /** + * @author 2No2Name + * @reason Avoid stream allocations + */ + @Overwrite + public Stream getSelfAndPassengers() { + if (this.passengers.isEmpty()) { + return Stream.of((Entity) (Object) this); + } + ArrayList passengers = new ArrayList<>(); + passengers.add((Entity) (Object) this); + this.addPassengersDeep(passengers); + + return passengers.stream(); + } + + /** + * @author 2No2Name + * @reason Avoid stream allocations + */ + @Overwrite + public Stream getPassengersAndSelf() { + if (this.passengers.isEmpty()) { + return Stream.of((Entity) (Object) this); + } + ArrayList passengers = new ArrayList<>(); + this.addPassengersDeepFirst(passengers); + passengers.add((Entity) (Object) this); + return passengers.stream(); + } + + private void addPassengersDeep(ArrayList passengers) { + ImmutableList list = this.passengers; + for (int i = 0, listSize = list.size(); i < listSize; i++) { + Entity passenger = list.get(i); + passengers.add(passenger); + //noinspection ConstantConditions + ((EntityMixin) (Object) passenger).addPassengersDeep(passengers); + } + } + + private void addPassengersDeepFirst(ArrayList passengers) { + ImmutableList list = this.passengers; + for (int i = 0, listSize = list.size(); i < listSize; i++) { + Entity passenger = list.get(i); + //noinspection ConstantConditions + ((EntityMixin) (Object) passenger).addPassengersDeep(passengers); + passengers.add(passenger); + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/collections/mob_spawning/PoolMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/collections/mob_spawning/PoolMixin.java new file mode 100644 index 0000000..6ca65db --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/collections/mob_spawning/PoolMixin.java @@ -0,0 +1,40 @@ +package net.gensokyoreimagined.nitori.mixin.needs_testing.collections.mob_spawning; + +import com.google.common.collect.ImmutableList; +import net.gensokyoreimagined.nitori.common.util.collections.HashedReferenceList; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.util.random.WeightedEntry; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Collections; +import java.util.List; + +@Mixin(WeightedRandomList.class) +public class PoolMixin { + + @Mutable + @Shadow + @Final + private ImmutableList items; + //Need a separate variable due to entries being type ImmutableList + @Unique + private List entryHashList; + + @Inject(method = "", at = @At("RETURN")) + private void init(List entries, CallbackInfo ci) { + //We are using reference equality here, because all vanilla implementations of Weighted use reference equality + this.entryHashList = this.items.size() > 4 ? Collections.unmodifiableList(new HashedReferenceList<>(this.items)) : this.items; + } + + /** + * @author 2No2Name + * @reason return a collection with a faster contains() call + */ + @Overwrite + public List unwrap() { + return this.entryHashList; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/network/block_breaking/CacheBlockBreakPacketMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/block_breaking/CacheBlockBreakPacketMixin.java new file mode 100644 index 0000000..230d592 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/block_breaking/CacheBlockBreakPacketMixin.java @@ -0,0 +1,33 @@ +package net.gensokyoreimagined.nitori.mixin.network.block_breaking; + +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +// Credit Mirai patch #0061 +@Mixin(ServerLevel.class) +public class CacheBlockBreakPacketMixin { + @Unique + private ClientboundBlockDestructionPacket packet; + + @Inject(method = "destroyBlockProgress", at = @At("HEAD")) + private void setPacketNull(int entityId, BlockPos pos, int progress, CallbackInfo ci) { + this.packet = null; + } + + @Redirect(method = "destroyBlockProgress", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;send(Lnet/minecraft/network/protocol/Packet;)V")) + private void redirectPacketSent(ServerGamePacketListenerImpl instance, Packet packetBad, @Local(ordinal = 0, argsOnly = true) int entityId, @Local(ordinal = 0, argsOnly = true) BlockPos pos, @Local(ordinal = 0, argsOnly = true) int progress, @Local(ordinal = 0)ServerPlayer serverPlayerEntity) { + if (this.packet == null) this.packet = new ClientboundBlockDestructionPacket(entityId, pos, progress); + serverPlayerEntity.connection.sendPacket(this.packet); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java index 7d0ea64..f6c6ec8 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java @@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.Inject; @Mixin(BlockBehaviour.BlockStateBase.class) public class AbstractBlockStateMixin implements BlockStateFlagHolder { @Unique - private int flags; + private int nitori$flags; @Inject(method = "initCache", at = @At("RETURN")) private void init(CallbackInfo ci) { @@ -34,11 +34,11 @@ public class AbstractBlockStateMixin implements BlockStateFlagHolder { } } - this.flags = flags; + this.nitori$flags = flags; } @Override public int lithium$getAllFlags() { - return this.flags; + return this.nitori$flags; } } diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/biome_access/BiomeAccessMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/biome_access/BiomeAccessMixin.java new file mode 100644 index 0000000..a5f9e1e --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/biome_access/BiomeAccessMixin.java @@ -0,0 +1,101 @@ +package net.gensokyoreimagined.nitori.mixin.world.biome_access; + +import net.minecraft.core.Holder; +import net.minecraft.util.Mth; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.util.LinearCongruentialGenerator; +import org.spongepowered.asm.mixin.*; + +/** + * Credit to FXMorin (Unmerged PR for Lithium) + */ +@Mixin(BiomeManager.class) +public class BiomeAccessMixin { + @Shadow + @Final + private BiomeManager.NoiseBiomeSource noiseBiomeSource; + + @Shadow + @Final + private long biomeZoomSeed; + + @Shadow + private static double getFiddle(long l) {return 0;} + + @Unique + private static final double nitori$maxOffset = 0.4500000001D; + + /** + * @author FX - PR0CESS + * @reason I wanted to make it faster + */ + @Overwrite + public Holder getBiome(BlockPos pos) { + int xMinus2 = pos.getX() - 2; + int yMinus2 = pos.getY() - 2; + int zMinus2 = pos.getZ() - 2; + int x = xMinus2 >> 2; + int y = yMinus2 >> 2; + int z = zMinus2 >> 2; + double quartX = (double)(xMinus2 & 3) / 4.0D; + double quartY = (double)(yMinus2 & 3) / 4.0D; + double quartZ = (double)(zMinus2 & 3) / 4.0D; + int smallestX = 0; + double smallestDist = Double.POSITIVE_INFINITY; + for(int biomeX = 0; biomeX < 8; ++biomeX) { + boolean everyOtherQuad = (biomeX & 4) == 0; + boolean everyOtherPair = (biomeX & 2) == 0; + boolean everyOther = (biomeX & 1) == 0; + double quartXX = everyOtherQuad ? quartX : quartX - 1.0D; + double quartYY = everyOtherPair ? quartY : quartY - 1.0D; + double quartZZ = everyOther ? quartZ : quartZ - 1.0D; + + double maxQuartYY = 0.0D,maxQuartZZ = 0.0D; + if (biomeX != 0) { + maxQuartYY = Mth.square(Math.max(quartYY + nitori$maxOffset, Math.abs(quartYY - nitori$maxOffset))); + maxQuartZZ = Mth.square(Math.max(quartZZ + nitori$maxOffset, Math.abs(quartZZ - nitori$maxOffset))); + double maxQuartXX = Mth.square(Math.max(quartXX + nitori$maxOffset, Math.abs(quartXX - nitori$maxOffset))); + if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) { + continue; + } + } + + int xx = everyOtherQuad ? x : x + 1; + int yy = everyOtherPair ? y : y + 1; + int zz = everyOther ? z : z + 1; + + long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx); + seed = LinearCongruentialGenerator.next(seed, yy); + seed = LinearCongruentialGenerator.next(seed, zz); + seed = LinearCongruentialGenerator.next(seed, xx); + seed = LinearCongruentialGenerator.next(seed, yy); + seed = LinearCongruentialGenerator.next(seed, zz); + double offsetX = getFiddle(seed); + double sqrX = Mth.square(quartXX + offsetX); + if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) { + continue; + } + seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); + double offsetY = getFiddle(seed); + double sqrY = Mth.square(quartYY + offsetY); + if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) { + continue; + } + seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); + double offsetZ = getFiddle(seed); + double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ); + + if (smallestDist > biomeDist) { + smallestX = biomeX; + smallestDist = biomeDist; + } + } + + int biomeX = (smallestX & 4) == 0 ? x : x + 1; + int biomeY = (smallestX & 2) == 0 ? y : y + 1; + int biomeZ = (smallestX & 1) == 0 ? z : z + 1; + return this.noiseBiomeSource.getNoiseBiome(biomeX, biomeY, biomeZ); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/blending/BlendMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/blending/BlendMixin.java new file mode 100644 index 0000000..db2fc72 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/blending/BlendMixin.java @@ -0,0 +1,16 @@ +package net.gensokyoreimagined.nitori.mixin.world.blending; + +import net.gensokyoreimagined.nitori.common.math.FasterMathUtil; +import net.minecraft.world.level.levelgen.blending.Blender; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +// Credit Gale patch #0019 +@Mixin(Blender.class) +public class BlendMixin { + @Redirect(method = "heightToOffset", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;positiveModulo(DD)D")) + private static double redirectBlendOffset(double dividend, double divisor) { + return FasterMathUtil.positiveModuloForPositiveIntegerDivisor(dividend, divisor); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/farmland/FarmlandBlockMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/farmland/FarmlandBlockMixin.java new file mode 100644 index 0000000..b87b1f4 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/farmland/FarmlandBlockMixin.java @@ -0,0 +1,40 @@ +package net.gensokyoreimagined.nitori.mixin.world.farmland; + +import net.minecraft.world.level.block.FarmBlock; +import net.minecraft.tags.FluidTags; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.chunk.LevelChunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +/** + * Credit to PaperMC patch #0682 and EinS4ckZwiebeln + */ +@Mixin(FarmBlock.class) +public abstract class FarmlandBlockMixin { + + /** + * @author QPCrummer + * @reason Optimize FarlandBlock nearby water lookup + */ + @Overwrite + private static boolean isNearWater(LevelReader world, BlockPos pos) { + int posX = pos.getX(); + int posY = pos.getY(); + int posZ = pos.getZ(); + + for (int dz = -4; dz <= 4; ++dz) { + int z = dz + posZ; + for (int dx = -4; dx <= 4; ++dx) { + int x = posX + dx; + for (int dy = 0; dy <= 1; ++dy) { + net.minecraft.world.level.chunk.LevelChunk chunk = (LevelChunk) world.getChunk(x >> 4, z >> 4); + net.minecraft.world.level.material.FluidState fluid = chunk.getBlockState(new BlockPos(x, dy + posY, z)).getFluidState(); + if (fluid.is(FluidTags.WATER)) return true; + } + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/tick_scheduler/ChunkTickSchedulerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/tick_scheduler/ChunkTickSchedulerMixin.java new file mode 100644 index 0000000..582a527 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/tick_scheduler/ChunkTickSchedulerMixin.java @@ -0,0 +1,294 @@ +package net.gensokyoreimagined.nitori.mixin.world.tick_scheduler; + +//TODO: Make this work with paper + + +//import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +//import it.unimi.dsi.fastutil.longs.Long2ReferenceAVLTreeMap; +//import it.unimi.dsi.fastutil.objects.ObjectIterator; +//import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +//import net.gensokyoreimagined.nitori.common.world.scheduler.OrderedTickQueue; +//import net.minecraft.nbt.ListTag; +//import net.minecraft.core.BlockPos; +//import net.minecraft.world.ticks.LevelChunkTicks; +//import net.minecraft.world.ticks.ScheduledTick; +//import net.minecraft.world.ticks.SavedTick; +//import net.minecraft.world.ticks.TickPriority; +//import org.jetbrains.annotations.Nullable; +//import org.spongepowered.asm.mixin.*; +//import org.spongepowered.asm.mixin.injection.At; +//import org.spongepowered.asm.mixin.injection.Inject; +//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +// +//import java.util.Collection; +//import java.util.List; +//import java.util.Queue; +//import java.util.Set; +//import java.util.function.BiConsumer; +//import java.util.function.Function; +//import java.util.function.Predicate; +//import java.util.stream.Stream; +// +//@Mixin(LevelChunkTicks.class) +//public class ChunkTickSchedulerMixin { +// private static volatile Reference2IntOpenHashMap TYPE_2_INDEX; +// +// static { +// TYPE_2_INDEX = new Reference2IntOpenHashMap<>(); +// TYPE_2_INDEX.defaultReturnValue(-1); +// } +// +// private final Long2ReferenceAVLTreeMap> tickQueuesByTimeAndPriority = new Long2ReferenceAVLTreeMap<>(); +// private OrderedTickQueue nextTickQueue; +// private final IntOpenHashSet allTicks = new IntOpenHashSet(); +// +// @Shadow +// private @Nullable BiConsumer, ScheduledTick> onTickAdded; +// +// @Mutable +// @Shadow +// @Final +// private Set> ticksPerPosition; +// +// @Shadow +// private @Nullable List> pendingTicks; +// +// @Mutable +// @Shadow +// @Final +// private Queue> tickQueue; +// +// // Paper start - add dirty flag +// private boolean dirty; +// private long lastSaved = Long.MIN_VALUE; +// +// public boolean isDirty(final long tick) { +// return this.dirty || (!this.tickQueue.isEmpty() && tick != this.lastSaved); +// } +// +// public void clearDirty() { +// this.dirty = false; +// } +// // Paper end - add dirty flag +// +// @Inject( +// method = {"()V", "(Ljava/util/List;)V"}, +// at = @At("RETURN") +// ) +// private void reinit(CallbackInfo ci) { +// //Remove replaced collections +// if (this.pendingTicks != null) { +// for (SavedTick orderedTick : this.pendingTicks) { +// this.allTicks.add(tickToInt(orderedTick.pos(), orderedTick.type())); +// } +// } +// this.ticksPerPosition = null; +// this.tickQueue = null; +// } +// +// private static int tickToInt(BlockPos pos, Object type) { +// //Y coordinate is 12 bits (BlockPos.toLong) +// //X and Z coordinate is 4 bits each (This scheduler is for a single chunk) +// //20 bits are in use for pos +// //12 bits remaining for the type, so up to 4096 different tickable blocks/fluids (not block states) -> can upgrade to long if needed +// int typeIndex = TYPE_2_INDEX.getInt(type); +// if (typeIndex == -1) { +// typeIndex = fixMissingType2Index(type); +// } +// +// int ret = ((pos.getX() & 0xF) << 16) | ((pos.getY() & (0xfff)) << 4) | (pos.getZ() & 0xF); +// ret |= typeIndex << 20; +// return ret; +// } +// +// //This method must be synchronized, otherwise type->int assignments can be overwritten and therefore change +// //Uses clone and volatile store to ensure only fully initialized maps are used, all threads share the same mapping +// private static synchronized int fixMissingType2Index(Object type) { +// //check again, other thread might have replaced the collection +// int typeIndex = TYPE_2_INDEX.getInt(type); +// if (typeIndex == -1) { +// Reference2IntOpenHashMap clonedType2Index = TYPE_2_INDEX.clone(); +// clonedType2Index.put(type, typeIndex = clonedType2Index.size()); +// TYPE_2_INDEX = clonedType2Index; +// if (typeIndex >= 4096) { +// throw new IllegalStateException("Lithium Tick Scheduler assumes at most 4096 different block types that receive scheduled ticks exist! Add mixin.world.tick_scheduler=false to the lithium properties/config to disable the optimization!"); +// } +// } +// return typeIndex; +// } +// +// /** +// * @author 2No2Name +// * @reason use faster collections +// */ +// @Overwrite +// public void schedule(ScheduledTick orderedTick) { +// int intTick = tickToInt(orderedTick.pos(), orderedTick.type()); +// if (this.allTicks.add(intTick)) { +// this.dirty = true; // Paper - add dirty flag +// this.scheduleUnchecked(orderedTick); +// } +// } +// +// // Computes a timestamped key including the tick's priority +// // Keys can be sorted in descending order to find what should be executed first +// // 60 time bits, 4 priority bits +// private static long getBucketKey(long time, TickPriority priority) { +// //using priority.ordinal() as is not negative instead of priority.index +// return (time << 4L) | (priority.ordinal() & 15); +// } +// +// private void updateNextTickQueue(boolean checkEmpty) { +// if (checkEmpty && this.nextTickQueue != null && this.nextTickQueue.isEmpty()) { +// OrderedTickQueue removed = this.tickQueuesByTimeAndPriority.remove(this.tickQueuesByTimeAndPriority.firstLongKey()); +// if (removed != this.nextTickQueue) { +// throw new IllegalStateException("Next tick queue doesn't have the lowest key!"); +// } +// } +// if (this.tickQueuesByTimeAndPriority.isEmpty()) { +// this.nextTickQueue = null; +// return; +// } +// long firstKey = this.tickQueuesByTimeAndPriority.firstLongKey(); +// this.nextTickQueue = this.tickQueuesByTimeAndPriority.get(firstKey); +// } +// +// /** +// * @author 2No2Name +// * @reason use faster collections +// */ +// @Overwrite +// @Nullable +// public ScheduledTick peek() { +// if (this.nextTickQueue == null) { +// return null; +// } +// return this.nextTickQueue.peek(); +// } +// +// /** +// * @author 2No2Name +// * @reason use faster collections +// */ +// @Overwrite +// @Nullable +// public ScheduledTick poll() { +// ScheduledTick ScheduledTick = this.nextTickQueue.poll(); +// if (ScheduledTick != null) { +// if (this.nextTickQueue.isEmpty()) { +// this.updateNextTickQueue(true); +// } +// this.allTicks.remove(tickToInt(ScheduledTick.pos(), ScheduledTick.type())); +// this.dirty = true; // Paper - add dirty flag +// return ScheduledTick; +// } +// return null; +// } +// +// +// private void scheduleUnchecked(ScheduledTick orderedTick) { +// OrderedTickQueue tickQueue = this.tickQueuesByTimeAndPriority.computeIfAbsent(getBucketKey(orderedTick.triggerTick(), orderedTick.priority()), key -> new OrderedTickQueue<>()); +// if (tickQueue.isEmpty()) { +// this.updateNextTickQueue(false); +// } +// tickQueue.offer(orderedTick); +// +// if (this.onTickAdded != null) { +// //noinspection unchecked +// this.onTickAdded.accept((LevelChunkTicks) (Object) this, orderedTick); +// } +// } +// +// /** +// * @author 2No2Name +// * @reason use faster collections +// */ +// @Overwrite +// public boolean hasScheduledTick(BlockPos pos, T type) { +// return this.allTicks.contains(tickToInt(pos, type)); +// } +// +// /** +// * @author 2No2Name +// * @reason use faster collections +// */ +// @Overwrite +// public void removeIf(Predicate> predicate) { +// for (ObjectIterator> tickQueueIterator = this.tickQueuesByTimeAndPriority.values().iterator(); tickQueueIterator.hasNext(); ) { +// OrderedTickQueue nextTickQueue = tickQueueIterator.next(); +// nextTickQueue.sort(); +// boolean removed = false; +// for (int i = 0; i < nextTickQueue.size(); i++) { +// ScheduledTick nextTick = nextTickQueue.getTickAtIndex(i); +// if (predicate.test(nextTick)) { +// nextTickQueue.setTickAtIndex(i, null); +// this.allTicks.remove(tickToInt(nextTick.pos(), nextTick.type())); +// removed = true; +// } +// } +// if (removed) { +// nextTickQueue.removeNullsAndConsumed(); +// } +// if (nextTickQueue.isEmpty()) { +// tickQueueIterator.remove(); +// } +// } +// this.updateNextTickQueue(false); +// } +// +// /** +// * @author 2No2Name +// * @reason use faster collections +// */ +// @Overwrite +// public Stream> getAll() { +// return this.tickQueuesByTimeAndPriority.values().stream().flatMap(Collection::stream); +// } +// +// +// /** +// * @author 2No2Name +// * @reason not use unused field +// */ +// @Overwrite +// public int count() { +// return this.allTicks.size(); +// } +// +// /** +// * @author 2No2Name +// * @reason not use unused field +// */ +// @Overwrite +// public ListTag save(long l, Function function) { +// this.lastSaved = l; +// ListTag ListTag = new ListTag(); +// if (this.pendingTicks != null) { +// for (SavedTick tick : this.pendingTicks) { +// ListTag.add(tick.save(function)); +// } +// } +// for (OrderedTickQueue nextTickQueue : this.tickQueuesByTimeAndPriority.values()) { +// for (ScheduledTick orderedTick : nextTickQueue) { +// ListTag.add(SavedTick.saveTick(orderedTick, function, l)); +// } +// } +// return ListTag; +// } +// +// +// /** +// * @author 2No2Name +// * @reason use our datastructures +// */ +// @Overwrite +// public void unpack(long time) { +// if (this.pendingTicks != null) { +// int i = -this.pendingTicks.size(); +// for (SavedTick tick : this.pendingTicks) { +// this.scheduleUnchecked(tick.unpack(time, i++)); +// } +// } +// this.pendingTicks = null; +// } +//} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index bade975..94f354a 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -30,6 +30,7 @@ "alloc.composter.ComposterMixin$ComposterBlockComposterInventoryMixin", "alloc.composter.ComposterMixin$ComposterBlockDummyInventoryMixin", "alloc.composter.ComposterMixin$ComposterBlockFullComposterInventoryMixin", + "alloc.biome_temprature_leak.Biome_threadLocalMixin", "cached_hashcode.BlockNeighborGroupMixin", "collections.attributes.AttributeContainerMixin", "collections.block_entity_tickers.WorldChunkMixin", @@ -38,10 +39,15 @@ "collections.entity_filtering.TypeFilterableListMixin", "collections.goals.GoalSelectorMixin", "collections.mob_spawning.SpawnSettingsMixin", + "collections.fluid_submersion.EntityMixin", + "collections.brain.BrainMixin", "entity.fast_hand_swing.LivingEntityMixin", "entity.fast_retrieval.SectionedEntityCacheMixin", "entity.fall_damage.NoFallDamageMixin", "entity.sprinting_particles.EntityMixin", + "entity.collisions.unpushable_cramming.BoatEntityMixin", + "entity.speed.EntitySpeedMixin", + "entity.navigation.CancelNavigationMixin", "logic.fast_bits_blockpos.OptimizedBlockPosBitsMixin", "math.fast_blockops.BlockPosMixin", "math.fast_blockops.DirectionMixin", @@ -55,8 +61,10 @@ "math.rounding.FastRoundingVoxShapeMixin", "math.sine_lut.MixinMth", "math.vec.FastMathVec3DMixin", + "math.random.RandomMixin", "network.microopt.VarIntsMixin", "network.microopt.StringEncodingMixin", + "network.block_breaking.CacheBlockBreakPacketMixin", "vmp.playerwatching.MixinChunkFilter", "vmp.playerwatching.MixinServerPlayerEntity", "vmp.playerwatching.optimize_nearby_player_lookups.MixinMobEntity", @@ -80,6 +88,9 @@ "world.block_entity_ticking.support_cache.BlockEntityMixin", "world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin", "world.block_entity_ticking.support_cache.WorldChunkMixin", - "world.portal_checks.DisablePortalChecksMixin" + "world.portal_checks.DisablePortalChecksMixin", + "world.blending.BlendMixin", + "world.farmland.FarmlandBlockMixin", + "world.biome_access.BiomeAccessMixin" ] }