From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: 2No2Name <50278648+2No2Name@users.noreply.github.com> Date: Sun, 21 Feb 2021 21:34:18 -0500 Subject: [PATCH] lithium: skip ticking block entities that are doing nothing diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningList.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningList.java new file mode 100644 index 0000000000000000000000000000000000000000..edcc9ec27cc7d8dc5bf04e1f70362b505742570c --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningList.java @@ -0,0 +1,255 @@ +package me.jellysquid.mods.lithium.common.util.collections; + +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +public class ListeningList implements List { + private final List delegate; + private final Runnable changeCallback; + + public ListeningList(List delegate, Runnable changeCallback) { + this.delegate = delegate; + this.changeCallback = changeCallback; + } + + private void onChange() { + this.changeCallback.run(); + } + + + @Override + public int size() { + return this.delegate.size(); + } + + @Override + public boolean isEmpty() { + return this.delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.delegate.contains(o); + } + + @NotNull + @Override + public Iterator iterator() { + return this.listIterator(); + } + + @NotNull + @Override + public Object[] toArray() { + return this.delegate.toArray(); + } + + @Override + public void forEach(Consumer consumer) { + this.delegate.forEach(consumer); + } + + @NotNull + @Override + public T1[] toArray(@NotNull T1[] t1s) { + //noinspection SuspiciousToArrayCall + return this.delegate.toArray(t1s); + } + + @Override + public boolean add(T t) { + boolean add = this.delegate.add(t); + this.onChange(); + //noinspection ConstantConditions + return add; + } + + @Override + public boolean remove(Object o) { + boolean remove = this.delegate.remove(o); + this.onChange(); + return remove; + } + + @Override + public boolean containsAll(@NotNull Collection collection) { + return this.delegate.containsAll(collection); + } + + @Override + public boolean addAll(@NotNull Collection collection) { + boolean addAll = this.delegate.addAll(collection); + this.onChange(); + return addAll; + } + + @Override + public boolean addAll(int i, @NotNull Collection collection) { + boolean addAll = this.delegate.addAll(i, collection); + this.onChange(); + return addAll; + } + + @Override + public boolean removeAll(@NotNull Collection collection) { + boolean b = this.delegate.removeAll(collection); + this.onChange(); + return b; + } + + @Override + public boolean removeIf(Predicate predicate) { + boolean b = this.delegate.removeIf(predicate); + this.onChange(); + return b; + } + + @Override + public boolean retainAll(@NotNull Collection collection) { + boolean b = this.delegate.retainAll(collection); + this.onChange(); + return b; + } + + @Override + public void replaceAll(UnaryOperator unaryOperator) { + this.delegate.replaceAll( unaryOperator); + this.onChange(); + } + + @Override + public void sort(Comparator comparator) { + this.delegate.sort(comparator); + this.onChange(); + } + + @Override + public void clear() { + this.delegate.clear(); + this.onChange(); + } + + @Override + public T get(int i) { + return this.delegate.get(i); + } + + @Override + public T set(int i, T t) { + T set = this.delegate.set(i, t); + this.onChange(); + return set; + } + + @Override + public void add(int i, T t) { + this.delegate.add(i, t); + this.onChange(); + } + + @Override + public T remove(int i) { + T remove = this.delegate.remove(i); + this.onChange(); + return remove; + } + + @Override + public int indexOf(Object o) { + return this.delegate.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return this.delegate.lastIndexOf(o); + } + + @NotNull + @Override + public ListIterator listIterator() { + return this.listIterator(0); + } + + @NotNull + @Override + public ListIterator listIterator(int i) { + return new ListIterator() { + final ListIterator itDelegate = ListeningList.this.delegate.listIterator(i); + + @Override + public boolean hasNext() { + return this.itDelegate.hasNext(); + } + + @Override + public T next() { + return this.itDelegate.next(); + } + + @Override + public boolean hasPrevious() { + return this.itDelegate.hasPrevious(); + } + + @Override + public T previous() { + return this.itDelegate.previous(); + } + + @Override + public int nextIndex() { + return this.itDelegate.nextIndex(); + } + + @Override + public int previousIndex() { + return this.itDelegate.previousIndex(); + } + + @Override + public void remove() { + this.itDelegate.remove(); + ListeningList.this.onChange(); + } + + @Override + public void set(T t) { + this.itDelegate.set(t); + ListeningList.this.onChange(); + + } + + @Override + public void add(T t) { + this.itDelegate.add(t); + ListeningList.this.onChange(); + } + }; + } + + @NotNull + @Override + public List subList(int i, int i1) { + throw new UnsupportedOperationException(); + } + + @Override + public Spliterator spliterator() { + return this.delegate.spliterator(); + } + + @Override + public Stream stream() { + return this.delegate.stream(); + } + + @Override + public Stream parallelStream() { + return this.delegate.parallelStream(); + } +} \ No newline at end of file diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/MaskedTickingBlockEntityList.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/MaskedTickingBlockEntityList.java new file mode 100644 index 0000000000000000000000000000000000000000..7f7e4810a9aca382d75862e56b0cf93157b54b92 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/MaskedTickingBlockEntityList.java @@ -0,0 +1,320 @@ +package me.jellysquid.mods.lithium.common.util.collections; + +import com.google.common.collect.Iterators; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Predicate; + +public class MaskedTickingBlockEntityList implements List { + private final Predicate mayContain; + + private final Reference2IntOpenHashMap allElements2Index; + private final ReferenceArrayList allElements; + + private final IntArrayList filteredSuccessor; + private final BitSet filteredElementsMask; + + private int firstRemovedIndex; + + //Visualization of the internal datastructures + //indices: 0 1 2 3 4 5 6 7 8 9 + //allElements: A B C D - F G H I J //E was fully removed, C,F,G,I were filtered away + //filteredMask: 1 1 0 1 0 0 0 1 0 1 + //successor: 0 1 3 - 7 - - - 9 - - (index offset by 1, due to the first successor having to point to the first element) + //Removals from the allElements ArrayList are done by setting the value to null + //The successor list is used to iterate through the allElements ArrayList with an increasing index, but skipping long chains of null elements. + //The BitSet mask is used to find the predecessor and successor quickly (not asymptotically fast, but fast enough) + + public MaskedTickingBlockEntityList(List list, Predicate mayContain) { + this.mayContain = mayContain; + this.allElements = new ReferenceArrayList<>(); + this.allElements2Index = new Reference2IntOpenHashMap<>(); + this.allElements2Index.defaultReturnValue(-1); + this.filteredSuccessor = new IntArrayList(); + this.filteredElementsMask = new BitSet(); + this.firstRemovedIndex = Integer.MAX_VALUE; + + for (T t : list) { + if (this.mayContain.test(t)) { + int index = this.allElements.size(); + this.allElements.add(t); + this.filteredElementsMask.set(index); + this.filteredSuccessor.add(index); + this.allElements2Index.put(t, index); + } + } + this.filteredSuccessor.add(-1); + } + + public void setEntryVisible(T t, boolean value) { + this.setEntryVisible(this.allElements2Index.getOrDefault(t, -1), value); + } + + public void setEntryVisible(int index, boolean value) { + //Visualization of the operations possible + //All: A B C D - F G H I J (- for null) + //filteredMask:1 1 0 1 0 0 0 1 0 1 + //indices: 0 1 2 3 4 5 6 7 8 9 + //successor: 0 1 3 - 7 - - - 9 - - (- for no successor) + //Set F visible: + //All: A B C D - F G H I J + //filteredMask:1 1 0 1 0 1 0 1 0 1 //set mask at F to 1 + //indices: 0 1 2 3 4 5 6 7 8 9 + //successor: 0 1 3 - 5 - 7 - 9 - - //update successor of predecessor to F and set F's successor to old successor of predecessor + //Set D filtered: + //All: A B C D - F G H I J + //Mask: 1 1 0 0 0 1 0 1 0 1 //set mask at D to 0 + //indices: 0 1 2 3 4 5 6 7 8 9 + //successor: 0 1 5 - - - 7 - 9 - - //update successor of predecessor to old successor of D and remove D's successor value + + //These calls do not modify the size, they can't cause rehashing, they are safe to use during iteration + if (index == -1 || value == this.filteredElementsMask.get(index)) { + return; + } + + this.filteredElementsMask.set(index, value); + int predecessor = this.filteredElementsMask.previousSetBit(index - 1); + if (value) { + int successor = this.filteredSuccessor.getInt(predecessor + 1); + this.filteredSuccessor.set(predecessor + 1, index); + this.filteredSuccessor.set(index + 1, successor); + } else { + int successor = this.filteredSuccessor.getInt(index + 1); + this.filteredSuccessor.set(predecessor + 1, successor); + this.filteredSuccessor.set(index + 1, -2); //no successor as this element cannot be reached + } + } + + private void compact() { + int targetSize = this.size(); + int newIndex = this.firstRemovedIndex - 1; + int lastVisible = this.filteredElementsMask.previousSetBit(newIndex); + + + for (int i = newIndex + 1; i < this.allElements.size(); i++) { + T t = this.allElements.get(i); + if (t == null) { + continue; + } + boolean visible = this.filteredElementsMask.get(i); + //shift all entries to the lower indices (filling the gaps created by remove() setting null) + newIndex++; + //i is guaranteed to not be smaller than newIndex, therefore we can write to the same collections + + this.allElements.set(newIndex, t); + this.allElements2Index.put(t, newIndex); + this.filteredElementsMask.set(newIndex, visible); + + //update the successor links + this.filteredSuccessor.set(newIndex + 1, -2); //no successor as there is no next entry yet + if (visible) { + this.filteredSuccessor.set(lastVisible + 1, newIndex); + lastVisible = newIndex; + } + } + + if (newIndex + 1 != targetSize) { + throw new IllegalStateException("Compaction ended up with incorrect size: Should be: " + targetSize + " but is: " + (newIndex + 1)); + } + + this.filteredSuccessor.set(lastVisible + 1, -1); //-1 means this was the last element + this.firstRemovedIndex = Integer.MAX_VALUE; + + this.filteredSuccessor.removeElements(targetSize + 1, this.filteredSuccessor.size()); + this.allElements.removeElements(targetSize, this.allElements.size()); + this.filteredElementsMask.clear(targetSize, this.filteredElementsMask.size()); + + this.filteredSuccessor.trim(targetSize * 4); + this.allElements.trim(targetSize * 4); + this.allElements2Index.trim(targetSize * 4); + } + + public Iterator filteredIterator() { + return new Iterator() { + int next = MaskedTickingBlockEntityList.this.filteredSuccessor.getInt(0); + T prev; + + @Override + public boolean hasNext() { + return this.next != -1; + } + + @Override + public T next() { + int next = this.next; + T prev = MaskedTickingBlockEntityList.this.allElements.get(next); + this.prev = prev; + this.next = MaskedTickingBlockEntityList.this.filteredSuccessor.getInt(next + 1); + return prev; + } + + @Override + public void remove() { + MaskedTickingBlockEntityList.this.remove(this.prev); + } + }; + } + + @Override + public int size() { + return this.allElements2Index.size(); + } + + @Override + public boolean isEmpty() { + return this.size() == 0; + } + + @Override + public boolean contains(Object o) { + //noinspection SuspiciousMethodCalls + return this.allElements2Index.containsKey(o); + } + + @Override + public @NotNull Iterator iterator() { + return Iterators.unmodifiableIterator(this.allElements2Index.keySet().iterator()); + } + + @Override + public Object[] toArray() { + return this.allElements2Index.keySet().toArray(); + } + + @NotNull + @Override + public T1[] toArray(@NotNull T1[] t1s) { + //noinspection SuspiciousToArrayCall + return this.allElements2Index.keySet().toArray(t1s); + } + + @Override + public boolean add(T t) { + int arraySize = this.allElements.size(); + int invalidEntries = arraySize - this.size(); + //Compaction is done during the add operation as it is guaranteed to not happen during iteration + if ((arraySize > 2048 && invalidEntries > (arraySize >> 1)) || arraySize >= Integer.MAX_VALUE - 1 && invalidEntries != 0) { + this.compact(); + } + + if (!this.mayContain.test(t)) { + return false; + } + + int index = this.allElements.size(); + int i = this.allElements2Index.putIfAbsent(t, index); + if (i != -1) { + return false; + } + this.allElements.add(t); + this.filteredSuccessor.add(0);//increase size so setEntryVisible doesn't crash with indexOutOfBounds + this.setEntryVisible(index, true); + + return true; + } + + @Override + public boolean remove(Object o) { + int index = this.allElements2Index.removeInt(o); + if (index == -1) { + return false; + } + this.setEntryVisible(index, false); + this.allElements.set(index, null); + this.firstRemovedIndex = Math.min(this.firstRemovedIndex, index); + return true; + } + + @Override + public boolean containsAll(@NotNull Collection c) { + return this.allElements2Index.keySet().containsAll(c); + } + + @Override + public boolean addAll(@NotNull Collection c) { + boolean b = false; + for (T t : c) { + this.add(t); + b = true; + } + return b; + } + + @Override + public boolean addAll(int index, @NotNull Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(@NotNull Collection c) { + boolean b = false; + for (Object t : c) { + b |= this.remove(t); + } + return b; + } + + @Override + public boolean retainAll(@NotNull Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + this.allElements2Index.clear(); + this.allElements.clear(); + this.filteredSuccessor.clear(); + this.filteredElementsMask.clear(); + this.firstRemovedIndex = Integer.MAX_VALUE; + this.filteredSuccessor.add(-1); + } + + @Override + public T get(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public T set(int index, T element) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(int index, T element) { + throw new UnsupportedOperationException(); + } + + @Override + public T remove(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public int indexOf(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public int lastIndexOf(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull ListIterator listIterator() { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull ListIterator listIterator(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull List subList(int fromIndex, int toIndex) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/blockentity/BlockEntitySleepTracker.java b/src/main/java/me/jellysquid/mods/lithium/common/world/blockentity/BlockEntitySleepTracker.java new file mode 100644 index 0000000000000000000000000000000000000000..c54e1e1f9c6b09022c0f20d5ea4d552bd3ceed55 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/world/blockentity/BlockEntitySleepTracker.java @@ -0,0 +1,7 @@ +package me.jellysquid.mods.lithium.common.world.blockentity; + +import net.minecraft.world.level.block.entity.TileEntity; + +public interface BlockEntitySleepTracker { + void setAwake(TileEntity tileEntity, boolean needsTicking); +} \ No newline at end of file diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/blockentity/SleepingBlockEntity.java b/src/main/java/me/jellysquid/mods/lithium/common/world/blockentity/SleepingBlockEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..4e765ab19ffb300b6c810333b2dc797637ae5c1b --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/world/blockentity/SleepingBlockEntity.java @@ -0,0 +1,7 @@ +package me.jellysquid.mods.lithium.common.world.blockentity; + +public interface SleepingBlockEntity { + default boolean canTickOnSide(boolean isClient) { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java index 183c9fb583cbd5392cb2c0f708e2b47dae4f12ff..45b52e197961eab3d2f3cd704a79d1aabda58452 100644 --- a/src/main/java/net/minecraft/world/level/World.java +++ b/src/main/java/net/minecraft/world/level/World.java @@ -97,8 +97,11 @@ import org.bukkit.event.block.BlockPhysicsEvent; // CraftBukkit end import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia +import me.jellysquid.mods.lithium.common.util.collections.MaskedTickingBlockEntityList; // Yatopia +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; // Yatopia +import me.jellysquid.mods.lithium.common.world.blockentity.SleepingBlockEntity; // Yatopia -public abstract class World implements GeneratorAccess, AutoCloseable, NonBlockingWorldAccess { // Yatopia +public abstract class World implements GeneratorAccess, AutoCloseable, NonBlockingWorldAccess, BlockEntitySleepTracker { // Yatopia protected static final Logger LOGGER = LogManager.getLogger(); public static final Codec> f = MinecraftKey.a.xmap(ResourceKey.b(IRegistry.L), ResourceKey::a); @@ -107,9 +110,17 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki public static final ResourceKey THE_END = ResourceKey.a(IRegistry.L, new MinecraftKey("the_end")); private static final EnumDirection[] a = EnumDirection.values(); //public final List tileEntityList = Lists.newArrayList(); // Paper - remove unused list - public final List tileEntityListTick = me.jellysquid.mods.lithium.common.util.collections.HashedList.wrapper(Lists.newArrayList()); // Yatopia + public List tileEntityListTick = me.jellysquid.mods.lithium.common.util.collections.HashedList.wrapper(Lists.newArrayList()); // Yatopia protected final List tileEntityListPending = me.jellysquid.mods.lithium.common.util.collections.HashedList.wrapper(Lists.newArrayList()); // Yatopia protected final java.util.Set tileEntityListUnload = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>()); // Airplane - use set with faster contains + // Yatopia start - lithium: skip ticking block entities that are doing nothing + private MaskedTickingBlockEntityList tileEntityListTick$lithium; + + @Override + public void setAwake(TileEntity tileEntity, boolean needsTicking) { + this.tileEntityListTick$lithium.setEntryVisible(tileEntity, needsTicking); + } + // Yatopia end public final Thread serverThread; private final boolean debugWorld; private int d; @@ -368,6 +379,8 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki this.keepSpawnInMemory = this.paperConfig.keepSpawnInMemory; // Paper this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime); this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime); + this.tileEntityListTick$lithium = new MaskedTickingBlockEntityList<>(this.tileEntityListTick, blockEntity -> ((SleepingBlockEntity) blockEntity).canTickOnSide(false)); // Yatopia + this.tileEntityListTick = tileEntityListTick$lithium; // Yatopia } // Paper start @@ -1032,6 +1045,15 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki } + // Yatopia start - lithium: skip ticking block entities that are doing nothing + public Iterator getAwakeBlockEntities(List list) { + if (list == this.tileEntityListTick && list instanceof MaskedTickingBlockEntityList) { + return ((MaskedTickingBlockEntityList) list).filteredIterator(); + } + return list.iterator(); + } + // Yatopia end + public void tickBlockEntities() { GameProfilerFiller gameprofilerfiller = this.getMethodProfiler(); @@ -1054,11 +1076,19 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki this.tickingTileEntities = true; // Spigot start + // Yatopia start - lithium: skip ticking block entities that are doing nothing // Iterator iterator = this.tileEntityListTick.iterator(); + Iterator iterator = getAwakeBlockEntities(this.tileEntityListTick); + int tilesThisCycle = 0; + while (iterator.hasNext()) { + TileEntity tileentity = (TileEntity) iterator.next(); + /* for (tileTickPosition = 0; tileTickPosition < tileEntityListTick.size(); tileTickPosition++) { // Paper - Disable tick limiters tileTickPosition = (tileTickPosition < tileEntityListTick.size()) ? tileTickPosition : 0; TileEntity tileentity = (TileEntity) this.tileEntityListTick.get(tileTickPosition); + */ + // Yatopia end // Spigot start if (tileentity == null) { getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash"); @@ -1096,8 +1126,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki throwable.printStackTrace(); getServer().getPluginManager().callEvent(new ServerExceptionEvent(new ServerInternalException(msg, throwable))); // Paper end - tilesThisCycle--; - this.tileEntityListTick.remove(tileTickPosition--); + iterator.remove(); // Yatopia continue; // Paper end // Spigot start @@ -1110,8 +1139,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki if (tileentity.isRemoved()) { // Spigot start - tilesThisCycle--; - this.tileEntityListTick.remove(tileTickPosition--); + iterator.remove(); // Yatopia // Spigot end //this.tileEntityList.remove(tileentity); // Paper - remove unused list // Paper - prevent double chunk lookups diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntity.java index 35c4d5414db66b977a354ac50d35a6aa0fcd4cf8..35aef3647c025eaa763e2b9a5a4e266d556725f5 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntity.java @@ -28,8 +28,9 @@ import org.bukkit.inventory.InventoryHolder; import org.spigotmc.CustomTimingsHandler; // Spigot import co.aikar.timings.MinecraftTimings; // Paper import co.aikar.timings.Timing; // Paper +import me.jellysquid.mods.lithium.common.world.blockentity.SleepingBlockEntity; -public abstract class TileEntity implements net.minecraft.server.KeyedObject { // Paper +public abstract class TileEntity implements net.minecraft.server.KeyedObject, SleepingBlockEntity { // Paper // Yatopia public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper // CraftBukkit start - data containers diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityBeehive.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityBeehive.java index d79094386a564a44d5a519477309f669ea045876..f714c1e4e4e70095e43ec86e76368d0ebc71887a 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityBeehive.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityBeehive.java @@ -23,6 +23,9 @@ import net.minecraft.world.level.block.BlockBeehive; import net.minecraft.world.level.block.BlockCampfire; import net.minecraft.world.level.block.BlockFire; import net.minecraft.world.level.block.state.IBlockData; +import me.jellysquid.mods.lithium.common.util.collections.ListeningList; +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; +import java.util.ArrayList; public class TileEntityBeehive extends TileEntity implements ITickable { @@ -30,11 +33,24 @@ public class TileEntityBeehive extends TileEntity implements ITickable { @Nullable public BlockPosition flowerPos = null; public int maxBees = net.pl3x.purpur.PurpurConfig.beeInsideBeeHive; // CraftBukkit - allow setting max amount of bees a hive can hold // Purpur - Change max bees inside beehive and bee_nest + public boolean doInit = true; // Yatopia + public boolean isTicking = true; // Yatopia public TileEntityBeehive() { super(TileEntityTypes.BEEHIVE); } + // Yatopia start - lithium: skip ticking block entities that are doing nothing + public void checkSleepState() { + if (this.world != null ) { + if ((this.bees.size() == 0) == this.isTicking) { + this.isTicking = !this.isTicking; + ((BlockEntitySleepTracker) this.world).setAwake(this, this.isTicking); + } + } + } + // Yatopia end + @Override public void update() { if (this.d()) { @@ -304,6 +320,12 @@ public class TileEntityBeehive extends TileEntity implements ITickable { @Override public void tick() { + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (this.doInit) { + this.doInit = false; + this.checkSleepState(); + } + // Yatopia end if (this.bees.size() == 0) { return; } // Yatopia - TE optimizations if (!this.world.isClientSide) { this.y(); @@ -324,6 +346,7 @@ public class TileEntityBeehive extends TileEntity implements ITickable { @Override public void load(IBlockData iblockdata, NBTTagCompound nbttagcompound) { super.load(iblockdata, nbttagcompound); + this.checkSleepState(); // Yatopia this.bees.clear(); NBTTagList nbttaglist = nbttagcompound.getList("Bees", 10); diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityBell.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityBell.java index 84f9f52c5b632621b509448ac1c760f64de6b062..c4c983b15c28db856be7335e53637a21af2c1b2f 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityBell.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityBell.java @@ -20,6 +20,7 @@ import net.minecraft.world.entity.player.EntityHuman; import net.minecraft.world.level.World; import net.minecraft.world.phys.AxisAlignedBB; import org.apache.commons.lang3.mutable.MutableInt; +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; public class TileEntityBell extends TileEntity implements ITickable { @@ -30,6 +31,8 @@ public class TileEntityBell extends TileEntity implements ITickable { private List h; private List getEntitiesAtRing() { return this.h; } // Paper - OBFHELPER private boolean i; private boolean getShouldReveal() { return this.i; } // Paper - OBFHELPER private int j; + public boolean ringing; // Yatopia + public boolean resonating; // Yatopia public TileEntityBell() { super(TileEntityTypes.BELL); @@ -37,6 +40,11 @@ public class TileEntityBell extends TileEntity implements ITickable { @Override public boolean setProperty(int i, int j) { + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (!this.ringing && i == 1 && this.world != null) { + ((BlockEntitySleepTracker) this.world).setAwake(this, true); + } + // Yatopia end if (i == 1) { this.f(); this.j = 0; @@ -81,6 +89,11 @@ public class TileEntityBell extends TileEntity implements ITickable { } } + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (!this.ringing && !this.resonating && this.world != null) { + ((BlockEntitySleepTracker)this.world).setAwake(this, false); + } + // Yatopia end } private void d() { @@ -88,6 +101,11 @@ public class TileEntityBell extends TileEntity implements ITickable { } public void a(EnumDirection enumdirection) { + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (!this.ringing && this.world != null) { + ((BlockEntitySleepTracker)this.world).setAwake(this, true); + } + // Yatopia end BlockPosition blockposition = this.getPosition(); this.c = enumdirection; diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityBrewingStand.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityBrewingStand.java index 95ca7d9e1eadbe5cc5674ed7352c1ed4d707363d..b5d5f38e520bb5d7912cc86d8ee25858bbd0786b 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityBrewingStand.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityBrewingStand.java @@ -34,6 +34,7 @@ import org.bukkit.event.inventory.BrewEvent; import org.bukkit.event.inventory.BrewingStandFuelEvent; import org.bukkit.inventory.InventoryHolder; // CraftBukkit end +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; public class TileEntityBrewingStand extends TileEntityContainer implements IWorldInventory, ITickable { @@ -50,6 +51,7 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl private int lastTick = MinecraftServer.currentTick; public List transaction = new java.util.ArrayList(); private int maxStack = 64; + public boolean isTicking = true; // Yatopia public void onOpen(CraftHumanEntity who) { transaction.add(who); @@ -72,6 +74,17 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl return maxStack; } + // Yatopia start - lithium: skip ticking block entities that are doing nothing + @Override + public void update() { + super.update(); + if (!this.isTicking && this.world != null) { + this.isTicking = true; + ((BlockEntitySleepTracker)this.world).setAwake(this, true); + } + } + // Yatopia end + public void setMaxStackSize(int size) { maxStack = size; } @@ -141,6 +154,12 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl @Override public void tick() { + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (this.brewTime == 0 && this.world != null) { + this.isTicking = false; + ((BlockEntitySleepTracker)this.world).setAwake(this, false); + } + // Yatopia end ItemStack itemstack = (ItemStack) this.items.get(4); if (this.fuelLevel <= 0 && itemstack.getItem() == Items.BLAZE_POWDER) { @@ -280,6 +299,12 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl @Override public void load(IBlockData iblockdata, NBTTagCompound nbttagcompound) { super.load(iblockdata, nbttagcompound); + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (!this.isTicking && this.world != null) { + this.isTicking = true; + ((BlockEntitySleepTracker) this.world).setAwake(this, true); + } + // Yatopia end this.items = NonNullList.a(this.getSize(), ItemStack.b); ContainerUtil.b(nbttagcompound, this.items); this.brewTime = nbttagcompound.getShort("BrewTime"); diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityCampfire.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityCampfire.java index 08759f461ec947c0d5655557f49d8717afee6f00..cdff01d4da417ef1be6238d3faaf464ee7884bd5 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityCampfire.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityCampfire.java @@ -27,12 +27,18 @@ import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.event.block.BlockCookEvent; // CraftBukkit end +import net.minecraft.core.NonNullList; +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; public class TileEntityCampfire extends TileEntity implements Clearable, ITickable { private final NonNullList items; public final int[] cookingTimes; public final int[] cookingTotalTimes; + // Yatopia start - lithium: skip ticking block entities that are doing nothing + public boolean isTicking = true; + public boolean doInit = true; + // Yatopia end public TileEntityCampfire() { super(TileEntityTypes.CAMPFIRE); @@ -41,8 +47,50 @@ public class TileEntityCampfire extends TileEntity implements Clearable, ITickab this.cookingTotalTimes = new int[4]; } + // Yatopia start - lithium: skip ticking block entities that are doing nothing + @Override + public void update() { + super.update(); + this.checkSleepState(); + } + + @Override + public void invalidateBlockCache() { + super.invalidateBlockCache(); + this.checkSleepState(); + } + + public void checkSleepState() { + if (this.world == null) { + return; + } + boolean shouldTick = false; + NonNullList beingCooked = this.items; + for (int i = 0; i < beingCooked.size(); i++) { + ItemStack stack = beingCooked.get(i); + if (!stack.isEmpty()) { + if (this.cookingTimes[i] > 0 || this.getBlock().get(BlockCampfire.LIT)) { + shouldTick = true; + break; + } + } + } + + if (shouldTick != this.isTicking) { + this.isTicking = shouldTick; + ((BlockEntitySleepTracker)this.world).setAwake(this, shouldTick); + } + } + // Yatopia end + @Override public void tick() { + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (this.doInit) { + this.doInit = false; + this.checkSleepState(); + } + // Yatopia end boolean flag = (Boolean) this.getBlock().get(BlockCampfire.LIT); boolean flag1 = this.world.isClientSide; @@ -149,6 +197,7 @@ public class TileEntityCampfire extends TileEntity implements Clearable, ITickab @Override public void load(IBlockData iblockdata, NBTTagCompound nbttagcompound) { super.load(iblockdata, nbttagcompound); + this.checkSleepState(); // Yatopia - lithium: skip ticking block entities that are doing nothing this.items.clear(); ContainerUtil.b(nbttagcompound, this.items); int[] aint; diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityChest.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityChest.java index 111f62d0e5b40e945793b8f504f2c035c0884a6a..130ffed3827d3716430414976a0e4f591598586c 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityChest.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityChest.java @@ -33,14 +33,27 @@ import net.minecraft.world.level.block.Blocks; import org.bukkit.craftbukkit.entity.CraftHumanEntity; import org.bukkit.entity.HumanEntity; // CraftBukkit end + import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; // Yatopia public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITickable private NonNullList items; protected float a; + private float getAnimationAngle() { return a; } // Yatopia - OBFHELPER protected float b; + private float getLastAnimationAngle() { return b; } // Yatopia - OBFHELPER public int viewingCount; private int j; + private int ticksOpen = j; // Yatopia - OBFHELPER + private int lastTime; // Yatopia + + // Yatopia start - lithium: skip ticking block entities that are doing nothing + public void checkWakeUp() { + if ((this.viewingCount != 0 || this.getAnimationAngle() != 0.0F || this.getLastAnimationAngle() != 0) && this.world != null) { + ((BlockEntitySleepTracker) this.world).setAwake(this, true); + } + } + // Yatopia end // CraftBukkit start - add fields and methods public List transaction = new java.util.ArrayList(); @@ -113,11 +126,27 @@ public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITic } public void tick() { + // Yatopia start - lithium: skip ticking block entities that are doing nothing + // noinspection ConstantConditions + int time = (int) this.world.getTime(); + // ticksOpen == 0 implies most likely that this is the first tick. We don't want to update the value then. + // overflow case is handled by not going to sleep when this.ticksOpen == 0 + if (this.ticksOpen != 0) { + this.ticksOpen += time - this.lastTime - 1; + } + this.lastTime = time; + // Yatopia end + int i = this.position.getX(); int j = this.position.getY(); int k = this.position.getZ(); ++this.j; + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (this.viewingCount == 0 && this.getAnimationAngle() == 0.0F && this.getLastAnimationAngle() == 0 && this.ticksOpen != 0 && this.world != null) { + ((BlockEntitySleepTracker) this.world).setAwake(this, false); + } + // Yatopia end } public void doOpenLogic() { @@ -223,6 +252,7 @@ public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITic @Override public boolean setProperty(int i, int j) { + this.checkWakeUp(); // Yatopia - lithium: skip ticking block entities that are doing nothing if (i == 1) { this.viewingCount = j; return true; @@ -254,7 +284,7 @@ public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITic // CraftBukkit end this.onOpen(); } - + this.checkWakeUp(); // Yatopia - lithium: skip ticking block entities that are doing nothing } @Override @@ -275,6 +305,7 @@ public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITic // CraftBukkit end this.onOpen(); } + this.checkWakeUp(); // Yatopia - lithium: skip ticking block entities that are doing nothing } diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityEnchantTable.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityEnchantTable.java index 4b1cb089355b455c6210f2df1af797cc363997cf..0049a675557cbc05e361c12e9fb84a38a3a21160 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityEnchantTable.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityEnchantTable.java @@ -9,6 +9,7 @@ import net.minecraft.util.MathHelper; import net.minecraft.world.INamableTileEntity; import net.minecraft.world.entity.player.EntityHuman; import net.minecraft.world.level.block.state.IBlockData; +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; public class TileEntityEnchantTable extends TileEntity implements INamableTileEntity, ITickable { @@ -51,6 +52,7 @@ public class TileEntityEnchantTable extends TileEntity implements INamableTileEn @Override public void tick() { if (!org.yatopiamc.yatopia.server.YatopiaConfig.shouldTickEnchantingTables) { return; } // Yatopia - TE optimizations + ((BlockEntitySleepTracker) this.world).setAwake(this, false); // Yatopia this.j = this.i; this.l = this.k; EntityHuman entityhuman = this.world.a((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D, 3.0D, false); diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityFurnace.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityFurnace.java index 8ec9ed518a3b5f70c5a5d74605a1f17d0f47b300..d8ef6d464ed32fe61dd9d1d5ff7c70994998f934 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityFurnace.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityFurnace.java @@ -53,9 +53,11 @@ import org.bukkit.event.inventory.FurnaceBurnEvent; import org.bukkit.event.inventory.FurnaceExtractEvent; import org.bukkit.event.inventory.FurnaceSmeltEvent; // CraftBukkit end +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; public abstract class TileEntityFurnace extends TileEntityContainer implements IWorldInventory, RecipeHolder, AutoRecipeOutput, ITickable { + private boolean isTicking = true; // Yatopia private static final int[] g = new int[]{0}; private static final int[] h = new int[]{2, 1}; private static final int[] i = new int[]{1}; @@ -197,6 +199,17 @@ public abstract class TileEntityFurnace extends TileEntityContainer implements I private int maxStack = MAX_STACK; public List transaction = new java.util.ArrayList(); + // Yatopia start - lithium: skip ticking block entities that are doing nothing + @Override + public void update() { + super.update(); + if (!this.isTicking && this.world != null) { + this.isTicking = true; + ((BlockEntitySleepTracker) this.world).setAwake(this, true); + } + } + // Yatopia end + public List getContents() { return this.items; } @@ -259,6 +272,12 @@ public abstract class TileEntityFurnace extends TileEntityContainer implements I @Override public void load(IBlockData iblockdata, NBTTagCompound nbttagcompound) { super.load(iblockdata, nbttagcompound); + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (!this.isTicking && this.world != null) { + this.isTicking = true; + ((BlockEntitySleepTracker)this.world).setAwake(this, true); + } + // Yatopia end this.items = NonNullList.a(this.getSize(), ItemStack.b); ContainerUtil.b(nbttagcompound, this.items); this.burnTime = nbttagcompound.getShort("BurnTime"); @@ -389,7 +408,12 @@ public abstract class TileEntityFurnace extends TileEntityContainer implements I if (flag1) { this.update(); } - + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (!this.isTicking && this.world != null) { + this.isTicking = true; + ((BlockEntitySleepTracker)this.world).setAwake(this, true); + } + // Yatopia end } protected boolean canBurn(@Nullable IRecipe irecipe) { diff --git a/src/main/java/net/minecraft/world/level/block/entity/TileEntityShulkerBox.java b/src/main/java/net/minecraft/world/level/block/entity/TileEntityShulkerBox.java index c1a90eb01689b8b91200ba49d58360d5ea2004c4..8095740f1848443e87fd52c14d19dabb00a3ecd4 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/TileEntityShulkerBox.java +++ b/src/main/java/net/minecraft/world/level/block/entity/TileEntityShulkerBox.java @@ -33,6 +33,7 @@ import net.minecraft.world.phys.shapes.VoxelShapes; import org.bukkit.craftbukkit.entity.CraftHumanEntity; import org.bukkit.entity.HumanEntity; // CraftBukkit end +import me.jellysquid.mods.lithium.common.world.blockentity.BlockEntitySleepTracker; public class TileEntityShulkerBox extends TileEntityLootable implements IWorldInventory, ITickable { @@ -41,7 +42,9 @@ public class TileEntityShulkerBox extends TileEntityLootable implements IWorldIn public int viewingCount; private TileEntityShulkerBox.AnimationPhase i; private float j; + public float animationProgress = j; // Yatopia - OBFHELPER private float k; + public float prevAnimationProgress = k; // Yatopia - OBFHELPER @Nullable private EnumColor l; private boolean m; @@ -96,6 +99,11 @@ public class TileEntityShulkerBox extends TileEntityLootable implements IWorldIn this.m(); } + // Yatopia start - lithium: skip ticking block entities that are doing nothing + if (this.getAnimationPhase() == TileEntityShulkerBox.AnimationPhase.CLOSED && this.prevAnimationProgress == 0f && this.animationProgress == 0f && this.world != null) { + ((BlockEntitySleepTracker) this.world).setAwake(this, false); + } + // Yatopia end } protected void h() { @@ -127,6 +135,7 @@ public class TileEntityShulkerBox extends TileEntityLootable implements IWorldIn } + public TileEntityShulkerBox.AnimationPhase getAnimationPhase() {return this.j(); } // Yatopia - OBFHELPER public TileEntityShulkerBox.AnimationPhase j() { return this.i; } @@ -209,6 +218,7 @@ public class TileEntityShulkerBox extends TileEntityLootable implements IWorldIn @Override public boolean setProperty(int i, int j) { + if (this.world != null && i == 1) ((BlockEntitySleepTracker) this.world).setAwake(this, true); // Yatopia if (i == 1) { this.viewingCount = j; if (j == 0) {