Merge branch 'refs/heads/master' into next
# Conflicts: # src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java # src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java
This commit is contained in:
57
README.md
57
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
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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<RandomGenerator.SplittableGenerator> 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("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T> extends AbstractQueue<ScheduledTick<T>> {
|
||||
// private static final int INITIAL_CAPACITY = 16;
|
||||
// private static final Comparator<ScheduledTick<?>> COMPARATOR = Comparator.comparingLong(ScheduledTick::subTickOrder);
|
||||
//
|
||||
// private ScheduledTick<T>[] arr;
|
||||
//
|
||||
// private int lastIndexExclusive;
|
||||
// private int firstIndex;
|
||||
//
|
||||
// private long currentMaxSubTickOrder = Long.MIN_VALUE;
|
||||
// private boolean isSorted;
|
||||
// private ScheduledTick<T> unsortedPeekResult;
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public OrderedTickQueue(int capacity) {
|
||||
// this.arr = (ScheduledTick<T>[]) 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<ScheduledTick<T>> 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<T> next() {
|
||||
// return OrderedTickQueue.this.arr[this.nextIndex++];
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ScheduledTick<T> poll() {
|
||||
// if (this.isEmpty()) {
|
||||
// return null;
|
||||
// }
|
||||
// if (!this.isSorted) {
|
||||
// this.sort();
|
||||
// }
|
||||
// ScheduledTick<T> nextTick;
|
||||
// int polledIndex = this.firstIndex++;
|
||||
// ScheduledTick<T>[] ticks = this.arr;
|
||||
// nextTick = ticks[polledIndex];
|
||||
// ticks[polledIndex] = null;
|
||||
// return nextTick;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public ScheduledTick<T> peek() {
|
||||
// if (!this.isSorted) {
|
||||
// return this.unsortedPeekResult;
|
||||
// } else if (this.lastIndexExclusive > this.firstIndex) {
|
||||
// return this.getTickAtIndex(this.firstIndex);
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// public boolean offer(ScheduledTick<T> 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<T> 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<T> 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<T> ScheduledTick = this.arr[src];
|
||||
// if (ScheduledTick != null) {
|
||||
// this.arr[dst] = ScheduledTick;
|
||||
// dst++;
|
||||
// }
|
||||
// src++;
|
||||
// }
|
||||
// this.handleCompaction(dst);
|
||||
// }
|
||||
//
|
||||
// public ScheduledTick<T> getTickAtIndex(int index) {
|
||||
// if (!this.isSorted) {
|
||||
// throw new IllegalStateException("Unexpected access on unsorted queue!");
|
||||
// }
|
||||
// return this.arr[index];
|
||||
// }
|
||||
//
|
||||
// public void setTickAtIndex(int index, ScheduledTick<T> tick) {
|
||||
// if (!this.isSorted) {
|
||||
// throw new IllegalStateException("Unexpected access on unsorted queue!");
|
||||
// }
|
||||
// this.arr[index] = tick;
|
||||
// }
|
||||
//
|
||||
// @SuppressWarnings("unchecked")
|
||||
// private static <T> ScheduledTick<T>[] copyArray(ScheduledTick<T>[] src, int size) {
|
||||
// final ScheduledTick<T>[] 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;
|
||||
// }
|
||||
//}
|
||||
@@ -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<Long2FloatLinkedOpenHashMap> memoryLeakFix$betterTempCache;
|
||||
|
||||
@WrapOperation(
|
||||
method = "<init>",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/lang/ThreadLocal;withInitial(Ljava/util/function/Supplier;)Ljava/lang/ThreadLocal;"
|
||||
)
|
||||
)
|
||||
private ThreadLocal<Long2FloatLinkedOpenHashMap> memoryLeakFix$useStaticThreadLocal(Supplier<?> supplier, Operation<ThreadLocal<Long2FloatLinkedOpenHashMap>> original) {
|
||||
if (memoryLeakFix$betterTempCache == null) {
|
||||
memoryLeakFix$betterTempCache = original.call(supplier);
|
||||
}
|
||||
return memoryLeakFix$betterTempCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.gensokyoreimagined.nitori.mixin.alloc;
|
||||
|
||||
/*
|
||||
/nbt/NbtCompoundMixin is not needed
|
||||
|
||||
*/
|
||||
@@ -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<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements;
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<TagKey<Fluid>> fluidOnEyes;
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Entity> getOtherPushableEntities(Level world, @Nullable Entity except, AABB box, Predicate<? super Entity> predicate) {
|
||||
//noinspection Guava
|
||||
if (predicate == Predicates.alwaysFalse()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (predicate instanceof EntityPushablePredicate<?> entityPushablePredicate) {
|
||||
EntitySectionStorage<Entity> cache = WorldHelper.getEntityCacheOrNull(world);
|
||||
if (cache != null) {
|
||||
//noinspection unchecked
|
||||
return WorldHelper.getPushableEntities(world, cache, except, box, (EntityPushablePredicate<? super Entity>) entityPushablePredicate);
|
||||
}
|
||||
}
|
||||
return world.getEntities(except, box, predicate);
|
||||
}
|
||||
}
|
||||
@@ -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<BlockPos> positions, Entity target, int range, boolean useHeadPos, int distance, float followRange, CallbackInfoReturnable<Path> cir) {
|
||||
if (mob.isPassenger()) {
|
||||
cir.setReturnValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<RandomSource> cir) {
|
||||
cir.setReturnValue(new RandomGeneratorRandom(seed));
|
||||
}
|
||||
|
||||
@Inject(method = "createNewThreadLocalInstance", at = @At(value = "HEAD"), cancellable = true)
|
||||
private static void fasterrandom$createLocalInject(@NotNull CallbackInfoReturnable<RandomSource> cir) {
|
||||
cir.setReturnValue(new RandomGeneratorRandom(ThreadLocalRandom.current().nextLong()));
|
||||
}
|
||||
|
||||
@Inject(method = "createThreadSafe", at = @At(value = "HEAD"), cancellable = true)
|
||||
private static void fasterrandom$createThreadSafeInject(@NotNull CallbackInfoReturnable<RandomSource> cir) {
|
||||
cir.setReturnValue(new RandomGeneratorRandom(RandomSupport.generateUniqueSeed()));
|
||||
}
|
||||
}
|
||||
@@ -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<Entity> passengers;
|
||||
|
||||
/**
|
||||
* @author 2No2Name
|
||||
* @reason Avoid stream code
|
||||
*/
|
||||
@Overwrite
|
||||
public Iterable<Entity> getIndirectPassengers() {
|
||||
if (this.passengers.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
ArrayList<Entity> passengers = new ArrayList<>();
|
||||
this.addPassengersDeep(passengers);
|
||||
|
||||
return passengers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author 2No2Name
|
||||
* @reason Avoid stream allocations
|
||||
*/
|
||||
@Overwrite
|
||||
private Stream<Entity> getIndirectPassengersStream() {
|
||||
if (this.passengers.isEmpty()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
ArrayList<Entity> passengers = new ArrayList<>();
|
||||
this.addPassengersDeep(passengers);
|
||||
|
||||
return passengers.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author 2No2Name
|
||||
* @reason Avoid stream allocations
|
||||
*/
|
||||
@Overwrite
|
||||
public Stream<Entity> getSelfAndPassengers() {
|
||||
if (this.passengers.isEmpty()) {
|
||||
return Stream.of((Entity) (Object) this);
|
||||
}
|
||||
ArrayList<Entity> passengers = new ArrayList<>();
|
||||
passengers.add((Entity) (Object) this);
|
||||
this.addPassengersDeep(passengers);
|
||||
|
||||
return passengers.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author 2No2Name
|
||||
* @reason Avoid stream allocations
|
||||
*/
|
||||
@Overwrite
|
||||
public Stream<Entity> getPassengersAndSelf() {
|
||||
if (this.passengers.isEmpty()) {
|
||||
return Stream.of((Entity) (Object) this);
|
||||
}
|
||||
ArrayList<Entity> passengers = new ArrayList<>();
|
||||
this.addPassengersDeepFirst(passengers);
|
||||
passengers.add((Entity) (Object) this);
|
||||
return passengers.stream();
|
||||
}
|
||||
|
||||
private void addPassengersDeep(ArrayList<Entity> passengers) {
|
||||
ImmutableList<Entity> 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<Entity> passengers) {
|
||||
ImmutableList<Entity> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<E extends WeightedEntry> {
|
||||
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
private ImmutableList<E> items;
|
||||
//Need a separate variable due to entries being type ImmutableList
|
||||
@Unique
|
||||
private List<E> entryHashList;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void init(List<? extends E> 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<E> unwrap() {
|
||||
return this.entryHashList;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Biome> 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<T> {
|
||||
// private static volatile Reference2IntOpenHashMap<Object> TYPE_2_INDEX;
|
||||
//
|
||||
// static {
|
||||
// TYPE_2_INDEX = new Reference2IntOpenHashMap<>();
|
||||
// TYPE_2_INDEX.defaultReturnValue(-1);
|
||||
// }
|
||||
//
|
||||
// private final Long2ReferenceAVLTreeMap<OrderedTickQueue<T>> tickQueuesByTimeAndPriority = new Long2ReferenceAVLTreeMap<>();
|
||||
// private OrderedTickQueue<T> nextTickQueue;
|
||||
// private final IntOpenHashSet allTicks = new IntOpenHashSet();
|
||||
//
|
||||
// @Shadow
|
||||
// private @Nullable BiConsumer<LevelChunkTicks<T>, ScheduledTick<T>> onTickAdded;
|
||||
//
|
||||
// @Mutable
|
||||
// @Shadow
|
||||
// @Final
|
||||
// private Set<ScheduledTick<?>> ticksPerPosition;
|
||||
//
|
||||
// @Shadow
|
||||
// private @Nullable List<SavedTick<T>> pendingTicks;
|
||||
//
|
||||
// @Mutable
|
||||
// @Shadow
|
||||
// @Final
|
||||
// private Queue<ScheduledTick<T>> 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 = {"<init>()V", "<init>(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<Object> 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<T> 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<T> 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<T> peek() {
|
||||
// if (this.nextTickQueue == null) {
|
||||
// return null;
|
||||
// }
|
||||
// return this.nextTickQueue.peek();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @author 2No2Name
|
||||
// * @reason use faster collections
|
||||
// */
|
||||
// @Overwrite
|
||||
// @Nullable
|
||||
// public ScheduledTick<T> poll() {
|
||||
// ScheduledTick<T> 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<T> orderedTick) {
|
||||
// OrderedTickQueue<T> 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<T>) (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<ScheduledTick<T>> predicate) {
|
||||
// for (ObjectIterator<OrderedTickQueue<T>> tickQueueIterator = this.tickQueuesByTimeAndPriority.values().iterator(); tickQueueIterator.hasNext(); ) {
|
||||
// OrderedTickQueue<T> nextTickQueue = tickQueueIterator.next();
|
||||
// nextTickQueue.sort();
|
||||
// boolean removed = false;
|
||||
// for (int i = 0; i < nextTickQueue.size(); i++) {
|
||||
// ScheduledTick<T> 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<ScheduledTick<T>> 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<T, String> function) {
|
||||
// this.lastSaved = l;
|
||||
// ListTag ListTag = new ListTag();
|
||||
// if (this.pendingTicks != null) {
|
||||
// for (SavedTick<T> tick : this.pendingTicks) {
|
||||
// ListTag.add(tick.save(function));
|
||||
// }
|
||||
// }
|
||||
// for (OrderedTickQueue<T> nextTickQueue : this.tickQueuesByTimeAndPriority.values()) {
|
||||
// for (ScheduledTick<T> 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<T> tick : this.pendingTicks) {
|
||||
// this.scheduleUnchecked(tick.unpack(time, i++));
|
||||
// }
|
||||
// }
|
||||
// this.pendingTicks = null;
|
||||
// }
|
||||
//}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user