diff --git a/patches/server/0102-Remove-stream-in-GateBehavior.patch b/patches/server/0102-Remove-stream-in-GateBehavior.patch new file mode 100644 index 000000000..c95810ad5 --- /dev/null +++ b/patches/server/0102-Remove-stream-in-GateBehavior.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Tue, 9 Nov 2077 00:00:00 +0800 +Subject: [PATCH] Remove stream in GateBehavior + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java +index d4581127366736c54f74e4ef7479236b18fb487d..67aa59deebdf47e53d9f6949b26c9b313de45035 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java +@@ -73,9 +73,20 @@ public class GateBehavior implements BehaviorControl + } + } + // Paper end - Perf: Remove streams from hot code +- if (this.behaviors.stream().noneMatch(task -> task.getStatus() == Behavior.Status.RUNNING)) { ++ ++ // Leaf start - Remove more streams in GateBehavior ++ boolean hasRunningTask = false; ++ for (final BehaviorControl task : this.behaviors) { ++ if (task.getStatus() == Behavior.Status.RUNNING) { ++ hasRunningTask = true; ++ break; ++ } ++ } ++ ++ if (!hasRunningTask) { + this.doStop(world, entity, time); + } ++ // Leaf end - Remove more streams in GateBehavior + } + + @Override diff --git a/patches/server/0102-Replace-Entity-active-effects-map-with-optimized-col.patch b/patches/server/0103-Replace-Entity-active-effects-map-with-optimized-col.patch similarity index 100% rename from patches/server/0102-Replace-Entity-active-effects-map-with-optimized-col.patch rename to patches/server/0103-Replace-Entity-active-effects-map-with-optimized-col.patch diff --git a/patches/server/0103-Replace-criterion-map-with-optimized-collection.patch b/patches/server/0104-Replace-criterion-map-with-optimized-collection.patch similarity index 100% rename from patches/server/0103-Replace-criterion-map-with-optimized-collection.patch rename to patches/server/0104-Replace-criterion-map-with-optimized-collection.patch diff --git a/patches/server/0104-Replace-world-map-with-optimized-collection.patch b/patches/server/0105-Replace-world-map-with-optimized-collection.patch similarity index 100% rename from patches/server/0104-Replace-world-map-with-optimized-collection.patch rename to patches/server/0105-Replace-world-map-with-optimized-collection.patch diff --git a/patches/server/0105-Replace-brain-maps-with-optimized-collection.patch b/patches/server/0106-Replace-brain-maps-with-optimized-collection.patch similarity index 100% rename from patches/server/0105-Replace-brain-maps-with-optimized-collection.patch rename to patches/server/0106-Replace-brain-maps-with-optimized-collection.patch diff --git a/patches/server/0106-Reduce-worldgen-allocations.patch b/patches/server/0107-Reduce-worldgen-allocations.patch similarity index 100% rename from patches/server/0106-Reduce-worldgen-allocations.patch rename to patches/server/0107-Reduce-worldgen-allocations.patch diff --git a/patches/server/0107-Use-caffeine-cache-kickPermission-instead-of-using-g.patch b/patches/server/0108-Use-caffeine-cache-kickPermission-instead-of-using-g.patch similarity index 100% rename from patches/server/0107-Use-caffeine-cache-kickPermission-instead-of-using-g.patch rename to patches/server/0108-Use-caffeine-cache-kickPermission-instead-of-using-g.patch diff --git a/patches/server/0108-Do-not-place-player-if-the-server-is-full.patch b/patches/server/0109-Do-not-place-player-if-the-server-is-full.patch similarity index 100% rename from patches/server/0108-Do-not-place-player-if-the-server-is-full.patch rename to patches/server/0109-Do-not-place-player-if-the-server-is-full.patch diff --git a/patches/server/0109-Fix-MC-65198.patch b/patches/server/0110-Fix-MC-65198.patch similarity index 100% rename from patches/server/0109-Fix-MC-65198.patch rename to patches/server/0110-Fix-MC-65198.patch diff --git a/patches/server/0110-Fix-MC-183518.patch b/patches/server/0111-Fix-MC-183518.patch similarity index 100% rename from patches/server/0110-Fix-MC-183518.patch rename to patches/server/0111-Fix-MC-183518.patch diff --git a/patches/server/0111-Fix-MC-200418.patch b/patches/server/0112-Fix-MC-200418.patch similarity index 100% rename from patches/server/0111-Fix-MC-200418.patch rename to patches/server/0112-Fix-MC-200418.patch diff --git a/patches/server/0112-Fix-MC-119417.patch b/patches/server/0113-Fix-MC-119417.patch similarity index 100% rename from patches/server/0112-Fix-MC-119417.patch rename to patches/server/0113-Fix-MC-119417.patch diff --git a/patches/server/0113-Fix-MC-223153.patch b/patches/server/0114-Fix-MC-223153.patch similarity index 100% rename from patches/server/0113-Fix-MC-223153.patch rename to patches/server/0114-Fix-MC-223153.patch diff --git a/patches/server/0114-Fix-MC-177381.patch b/patches/server/0115-Fix-MC-177381.patch similarity index 100% rename from patches/server/0114-Fix-MC-177381.patch rename to patches/server/0115-Fix-MC-177381.patch diff --git a/patches/server/0115-Optimize-LeavesProtocolManager-init-protocol.patch b/patches/server/0116-Optimize-LeavesProtocolManager-init-protocol.patch similarity index 100% rename from patches/server/0115-Optimize-LeavesProtocolManager-init-protocol.patch rename to patches/server/0116-Optimize-LeavesProtocolManager-init-protocol.patch diff --git a/patches/server/0116-Cache-CraftEntityType-minecraftToBukkit-convert.patch b/patches/server/0117-Cache-CraftEntityType-minecraftToBukkit-convert.patch similarity index 100% rename from patches/server/0116-Cache-CraftEntityType-minecraftToBukkit-convert.patch rename to patches/server/0117-Cache-CraftEntityType-minecraftToBukkit-convert.patch diff --git a/patches/server/0117-Configurable-player-knockback-zombie.patch b/patches/server/0118-Configurable-player-knockback-zombie.patch similarity index 100% rename from patches/server/0117-Configurable-player-knockback-zombie.patch rename to patches/server/0118-Configurable-player-knockback-zombie.patch diff --git a/patches/server/0118-Hide-specified-item-components-to-clients.patch b/patches/server/0119-Hide-specified-item-components-to-clients.patch similarity index 100% rename from patches/server/0118-Hide-specified-item-components-to-clients.patch rename to patches/server/0119-Hide-specified-item-components-to-clients.patch diff --git a/patches/server/0119-Paper-PR-Skip-AI-during-inactive-ticks-for-non-aware.patch b/patches/server/0120-Paper-PR-Skip-AI-during-inactive-ticks-for-non-aware.patch similarity index 100% rename from patches/server/0119-Paper-PR-Skip-AI-during-inactive-ticks-for-non-aware.patch rename to patches/server/0120-Paper-PR-Skip-AI-during-inactive-ticks-for-non-aware.patch diff --git a/patches/server/0120-Paper-PR-Reduce-work-done-in-CraftMapCanvas.drawImag.patch b/patches/server/0121-Paper-PR-Reduce-work-done-in-CraftMapCanvas.drawImag.patch similarity index 100% rename from patches/server/0120-Paper-PR-Reduce-work-done-in-CraftMapCanvas.drawImag.patch rename to patches/server/0121-Paper-PR-Reduce-work-done-in-CraftMapCanvas.drawImag.patch diff --git a/patches/server/0121-Paper-PR-Throttle-failed-spawn-attempts.patch b/patches/server/0122-Paper-PR-Throttle-failed-spawn-attempts.patch similarity index 100% rename from patches/server/0121-Paper-PR-Throttle-failed-spawn-attempts.patch rename to patches/server/0122-Paper-PR-Throttle-failed-spawn-attempts.patch diff --git a/patches/server/0122-Paper-PR-Prevent-zombie-reinforcements-loading-chunk.patch b/patches/server/0123-Paper-PR-Prevent-zombie-reinforcements-loading-chunk.patch similarity index 100% rename from patches/server/0122-Paper-PR-Prevent-zombie-reinforcements-loading-chunk.patch rename to patches/server/0123-Paper-PR-Prevent-zombie-reinforcements-loading-chunk.patch diff --git a/patches/server/0123-Dont-send-useless-entity-packets.patch b/patches/server/0124-Dont-send-useless-entity-packets.patch similarity index 100% rename from patches/server/0123-Dont-send-useless-entity-packets.patch rename to patches/server/0124-Dont-send-useless-entity-packets.patch diff --git a/patches/server/0124-Don-t-spawn-if-lastSpawnState-is-null.patch b/patches/server/0125-Don-t-spawn-if-lastSpawnState-is-null.patch similarity index 100% rename from patches/server/0124-Don-t-spawn-if-lastSpawnState-is-null.patch rename to patches/server/0125-Don-t-spawn-if-lastSpawnState-is-null.patch diff --git a/patches/server/0125-Multithreaded-Tracker.patch b/patches/server/0126-Multithreaded-Tracker.patch similarity index 100% rename from patches/server/0125-Multithreaded-Tracker.patch rename to patches/server/0126-Multithreaded-Tracker.patch diff --git a/patches/server/0126-Nitori-Async-playerdata-Save.patch b/patches/server/0127-Nitori-Async-playerdata-Save.patch similarity index 100% rename from patches/server/0126-Nitori-Async-playerdata-Save.patch rename to patches/server/0127-Nitori-Async-playerdata-Save.patch diff --git a/patches/server/0127-Change-max-stack-count.patch b/patches/server/0128-Change-max-stack-count.patch similarity index 100% rename from patches/server/0127-Change-max-stack-count.patch rename to patches/server/0128-Change-max-stack-count.patch diff --git a/patches/server/0128-Reduce-object-complexity-to-make-block-isValid-calls.patch b/patches/server/0129-Reduce-object-complexity-to-make-block-isValid-calls.patch similarity index 100% rename from patches/server/0128-Reduce-object-complexity-to-make-block-isValid-calls.patch rename to patches/server/0129-Reduce-object-complexity-to-make-block-isValid-calls.patch diff --git a/patches/server/0129-Optimize-nearby-alive-players-for-spawning.patch b/patches/server/0130-Optimize-nearby-alive-players-for-spawning.patch similarity index 100% rename from patches/server/0129-Optimize-nearby-alive-players-for-spawning.patch rename to patches/server/0130-Optimize-nearby-alive-players-for-spawning.patch diff --git a/patches/server/0130-Cache-blockstate-cache.patch b/patches/server/0131-Cache-blockstate-cache.patch similarity index 100% rename from patches/server/0130-Cache-blockstate-cache.patch rename to patches/server/0131-Cache-blockstate-cache.patch diff --git a/patches/server/0131-Asynchronous-locator.patch b/patches/server/0132-Asynchronous-locator.patch similarity index 100% rename from patches/server/0131-Asynchronous-locator.patch rename to patches/server/0132-Asynchronous-locator.patch diff --git a/patches/server/0132-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/patches/server/0133-Smart-sort-entities-in-NearestLivingEntitySensor.patch similarity index 100% rename from patches/server/0132-Smart-sort-entities-in-NearestLivingEntitySensor.patch rename to patches/server/0133-Smart-sort-entities-in-NearestLivingEntitySensor.patch diff --git a/patches/server/0133-Further-reduce-memory-footprint-of-CompoundTag.patch b/patches/server/0134-Further-reduce-memory-footprint-of-CompoundTag.patch similarity index 100% rename from patches/server/0133-Further-reduce-memory-footprint-of-CompoundTag.patch rename to patches/server/0134-Further-reduce-memory-footprint-of-CompoundTag.patch diff --git a/patches/server/0134-Optimize-Entity-distanceToSqr.patch b/patches/server/0135-Optimize-Entity-distanceToSqr.patch similarity index 100% rename from patches/server/0134-Optimize-Entity-distanceToSqr.patch rename to patches/server/0135-Optimize-Entity-distanceToSqr.patch diff --git a/patches/server/0135-EMC-Don-t-use-snapshots-for-TileEntity-getOwner.patch b/patches/server/0136-EMC-Don-t-use-snapshots-for-TileEntity-getOwner.patch similarity index 100% rename from patches/server/0135-EMC-Don-t-use-snapshots-for-TileEntity-getOwner.patch rename to patches/server/0136-EMC-Don-t-use-snapshots-for-TileEntity-getOwner.patch diff --git a/patches/server/0136-EMC-Default-don-t-use-blockstate-snapshots.patch b/patches/server/0137-EMC-Default-don-t-use-blockstate-snapshots.patch similarity index 100% rename from patches/server/0136-EMC-Default-don-t-use-blockstate-snapshots.patch rename to patches/server/0137-EMC-Default-don-t-use-blockstate-snapshots.patch diff --git a/patches/server/0137-Cache-tile-entity-position.patch b/patches/server/0138-Cache-tile-entity-position.patch similarity index 100% rename from patches/server/0137-Cache-tile-entity-position.patch rename to patches/server/0138-Cache-tile-entity-position.patch diff --git a/patches/server/0138-TT20-Lag-compensation.patch b/patches/server/0139-TT20-Lag-compensation.patch similarity index 100% rename from patches/server/0138-TT20-Lag-compensation.patch rename to patches/server/0139-TT20-Lag-compensation.patch diff --git a/patches/server/0139-Fix-wrong-entity-behavior-in-fluid-caused-by-inconsi.patch b/patches/server/0140-Fix-wrong-entity-behavior-in-fluid-caused-by-inconsi.patch similarity index 100% rename from patches/server/0139-Fix-wrong-entity-behavior-in-fluid-caused-by-inconsi.patch rename to patches/server/0140-Fix-wrong-entity-behavior-in-fluid-caused-by-inconsi.patch diff --git a/patches/server/0140-C2ME-Reduce-Allocations.patch b/patches/server/0141-C2ME-Reduce-Allocations.patch similarity index 100% rename from patches/server/0140-C2ME-Reduce-Allocations.patch rename to patches/server/0141-C2ME-Reduce-Allocations.patch diff --git a/patches/server/0141-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch b/patches/server/0142-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch similarity index 100% rename from patches/server/0141-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch rename to patches/server/0142-Lithium-Skip-unnecessary-calculations-if-player-is-n.patch diff --git a/patches/server/0142-Lithium-fast-util.patch b/patches/server/0143-Lithium-fast-util.patch similarity index 100% rename from patches/server/0142-Lithium-fast-util.patch rename to patches/server/0143-Lithium-fast-util.patch diff --git a/patches/server/0143-Lithium-CompactSineLUT.patch b/patches/server/0144-Lithium-CompactSineLUT.patch similarity index 100% rename from patches/server/0143-Lithium-CompactSineLUT.patch rename to patches/server/0144-Lithium-CompactSineLUT.patch diff --git a/patches/server/0144-Lithium-IterateOutwardsCache.patch b/patches/server/0145-Lithium-IterateOutwardsCache.patch similarity index 100% rename from patches/server/0144-Lithium-IterateOutwardsCache.patch rename to patches/server/0145-Lithium-IterateOutwardsCache.patch diff --git a/patches/server/0145-Lithium-HashedList.patch b/patches/server/0146-Lithium-HashedList.patch similarity index 100% rename from patches/server/0145-Lithium-HashedList.patch rename to patches/server/0146-Lithium-HashedList.patch diff --git a/patches/server/0146-Use-MCUtil.asyncExecutor-for-MAIN_WORKER_EXECUTOR.patch b/patches/server/0147-Use-MCUtil.asyncExecutor-for-MAIN_WORKER_EXECUTOR.patch similarity index 100% rename from patches/server/0146-Use-MCUtil.asyncExecutor-for-MAIN_WORKER_EXECUTOR.patch rename to patches/server/0147-Use-MCUtil.asyncExecutor-for-MAIN_WORKER_EXECUTOR.patch diff --git a/patches/server/0147-Better-inline-world-height.patch b/patches/server/0148-Better-inline-world-height.patch similarity index 100% rename from patches/server/0147-Better-inline-world-height.patch rename to patches/server/0148-Better-inline-world-height.patch diff --git a/patches/server/0148-Branchless-clamp-logic.patch b/patches/server/0149-Branchless-clamp-logic.patch similarity index 100% rename from patches/server/0148-Branchless-clamp-logic.patch rename to patches/server/0149-Branchless-clamp-logic.patch diff --git a/patches/server/0149-Use-faster-and-thread-safe-ban-list-date-format-pars.patch b/patches/server/0150-Use-faster-and-thread-safe-ban-list-date-format-pars.patch similarity index 100% rename from patches/server/0149-Use-faster-and-thread-safe-ban-list-date-format-pars.patch rename to patches/server/0150-Use-faster-and-thread-safe-ban-list-date-format-pars.patch diff --git a/patches/server/0151-Collect-then-startEachNonRunningBehavior-in-Brain.patch b/patches/server/0151-Collect-then-startEachNonRunningBehavior-in-Brain.patch new file mode 100644 index 000000000..e0edd6de8 --- /dev/null +++ b/patches/server/0151-Collect-then-startEachNonRunningBehavior-in-Brain.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Tue, 9 Nov 2077 00:00:00 +0800 +Subject: [PATCH] Collect then startEachNonRunningBehavior in Brain + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/Brain.java b/src/main/java/net/minecraft/world/entity/ai/Brain.java +index 65bd8c2cccd0a4a68984ea8ff4cd3cf365330630..aeb74b96704e0f187178e2ae106e0a3f6d95717c 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/Brain.java ++++ b/src/main/java/net/minecraft/world/entity/ai/Brain.java +@@ -466,20 +466,30 @@ public class Brain { + } + + private void startEachNonRunningBehavior(ServerLevel world, E entity) { +- long l = world.getGameTime(); ++ // Leaf start - Collect first then start ++ final long gameTime = world.getGameTime(); + +- for (Map>> map : this.availableBehaviorsByPriority.values()) { +- for (Entry>> entry : map.entrySet()) { +- Activity activity = entry.getKey(); +- if (this.activeActivities.contains(activity)) { +- for (BehaviorControl behaviorControl : entry.getValue()) { +- if (behaviorControl.getStatus() == Behavior.Status.STOPPED) { +- behaviorControl.tryStart(world, entity, l); ++ List> behaviorsToStart = new ObjectArrayList<>(); ++ ++ for (Activity activeActivity : this.activeActivities) { ++ for (Map>> priorityMap : this.availableBehaviorsByPriority.values()) { ++ Set> behaviors = priorityMap.get(activeActivity); ++ ++ if (behaviors != null && !behaviors.isEmpty()) { ++ for (BehaviorControl behavior : behaviors) { ++ if (behavior.getStatus() == Behavior.Status.STOPPED) { ++ behaviorsToStart.add(behavior); + } + } + } + } + } ++ if (!behaviorsToStart.isEmpty()) { ++ for (BehaviorControl behavior : behaviorsToStart) { ++ behavior.tryStart(world, entity, gameTime); ++ } ++ } ++ // Leaf end - Collect first then start + } + + private void tickEachRunningBehavior(ServerLevel world, E entity) { diff --git a/patches/server/0152-Lithium-equipment-tracking.patch b/patches/server/0152-Lithium-equipment-tracking.patch new file mode 100644 index 000000000..761870ce4 --- /dev/null +++ b/patches/server/0152-Lithium-equipment-tracking.patch @@ -0,0 +1,741 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> +Date: Tue, 9 Nov 2077 00:00:00 +0800 +Subject: [PATCH] Lithium: equipment tracking + +This patch is based on the following mixins: +* "net/caffeinemc/mods/lithium/mixin/util/ +item_component_and_count_tracking/PatchedDataComponentMapMixin.java" +* "net/caffeinemc/mods/lithium/mixin/util/item_component_and_count_tracking/ItemStackMixin.java" +* "net/caffeinemc/mods/lithium/mixin/entity/ +equipment_tracking/equipment_changes/LivingEntityMixin.java" +* "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/ArmorStandMixin.java" +* "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/LivingEntityMixin.java" +* "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/MobMixin.java" +* "net/caffeinemc/mods/lithium/mixin/entity/equipment_tracking/ +enchantment_ticking/LivingEntityMixin.java" +* "net/caffeinemc/mods/lithium/common/entity/EquipmentEntity.java" +* "net/caffeinemc/mods/lithium/common/util/change_tracking/ChangePublisher.java" +* "net/caffeinemc/mods/lithium/common/util/change_tracking/ChangeSubscriber.java" + +As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) +Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) + +diff --git a/src/main/java/net/caffeinemc/mods/lithium/common/entity/EquipmentEntity.java b/src/main/java/net/caffeinemc/mods/lithium/common/entity/EquipmentEntity.java +new file mode 100644 +index 0000000000000000000000000000000000000000..05604b8f149c13454983d29a2c4dfe1e11dec1fb +--- /dev/null ++++ b/src/main/java/net/caffeinemc/mods/lithium/common/entity/EquipmentEntity.java +@@ -0,0 +1,16 @@ ++package net.caffeinemc.mods.lithium.common.entity; ++ ++import net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber; ++import net.minecraft.world.item.ItemStack; ++ ++public interface EquipmentEntity { ++ void onEquipmentReplaced(ItemStack oldStack, ItemStack newStack); ++ ++ interface EquipmentTrackingEntity { ++ void onEquipmentChanged(); ++ } ++ ++ interface TickableEnchantmentTrackingEntity extends ChangeSubscriber.EnchantmentSubscriber { ++ void updateHasTickableEnchantments(ItemStack oldStack, ItemStack newStack); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/caffeinemc/mods/lithium/common/util/change_tracking/ChangePublisher.java b/src/main/java/net/caffeinemc/mods/lithium/common/util/change_tracking/ChangePublisher.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9a39b58e8aa3a472a9b5a5af1e5ee6d4eaa4d6e4 +--- /dev/null ++++ b/src/main/java/net/caffeinemc/mods/lithium/common/util/change_tracking/ChangePublisher.java +@@ -0,0 +1,17 @@ ++package net.caffeinemc.mods.lithium.common.util.change_tracking; ++ ++import net.minecraft.world.item.ItemStack; ++ ++public interface ChangePublisher { ++ void subscribe(ChangeSubscriber subscriber, int subscriberData); ++ ++ int unsubscribe(ChangeSubscriber subscriber); ++ ++ default void unsubscribeWithData(ChangeSubscriber subscriber, int index) { ++ throw new UnsupportedOperationException("Only implemented for ItemStacks"); ++ } ++ ++ default boolean isSubscribedWithData(ChangeSubscriber subscriber, int subscriberData) { ++ throw new UnsupportedOperationException("Only implemented for ItemStacks"); ++ } ++} +diff --git a/src/main/java/net/caffeinemc/mods/lithium/common/util/change_tracking/ChangeSubscriber.java b/src/main/java/net/caffeinemc/mods/lithium/common/util/change_tracking/ChangeSubscriber.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ef206b9ebbd555a786dad37e1ab1bc48e11961cb +--- /dev/null ++++ b/src/main/java/net/caffeinemc/mods/lithium/common/util/change_tracking/ChangeSubscriber.java +@@ -0,0 +1,190 @@ ++package net.caffeinemc.mods.lithium.common.util.change_tracking; ++ ++import it.unimi.dsi.fastutil.ints.IntArrayList; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.ArrayList; ++import net.minecraft.world.item.ItemStack; ++ ++public interface ChangeSubscriber { ++ ++ static ChangeSubscriber combine(ChangeSubscriber prevSubscriber, int prevSData, @NotNull ChangeSubscriber newSubscriber, int newSData) { ++ if (prevSubscriber == null) { ++ return newSubscriber; ++ } else if (prevSubscriber instanceof Multi) { ++ ArrayList> subscribers = new ArrayList<>(((Multi) prevSubscriber).subscribers); ++ IntArrayList subscriberDatas = new IntArrayList(((Multi) prevSubscriber).subscriberDatas); ++ subscribers.add(newSubscriber); ++ subscriberDatas.add(newSData); ++ return new Multi<>(subscribers, subscriberDatas); ++ } else { ++ ArrayList> subscribers = new ArrayList<>(); ++ IntArrayList subscriberDatas = new IntArrayList(); ++ subscribers.add(prevSubscriber); ++ subscriberDatas.add(prevSData); ++ subscribers.add(newSubscriber); ++ subscriberDatas.add(newSData); ++ return new Multi<>(subscribers, subscriberDatas); ++ } ++ } ++ static ChangeSubscriber without(ChangeSubscriber prevSubscriber, ChangeSubscriber removedSubscriber) { ++ return without(prevSubscriber, removedSubscriber, 0, false); ++ } ++ ++ static ChangeSubscriber without(ChangeSubscriber prevSubscriber, ChangeSubscriber removedSubscriber, int removedSubscriberData, boolean matchData) { ++ if (prevSubscriber == removedSubscriber) { ++ return null; ++ } else if (prevSubscriber instanceof Multi multi) { ++ int index = multi.indexOf(removedSubscriber, removedSubscriberData, matchData); ++ if (index != -1) { ++ if (multi.subscribers.size() == 2) { ++ return multi.subscribers.get(1 - index); ++ } else { ++ ArrayList> subscribers = new ArrayList<>(multi.subscribers); ++ IntArrayList subscriberDatas = new IntArrayList(multi.subscriberDatas); ++ subscribers.remove(index); ++ subscriberDatas.removeInt(index); ++ ++ return new Multi<>(subscribers, subscriberDatas); ++ } ++ } else { ++ return prevSubscriber; ++ } ++ } else { ++ return prevSubscriber; ++ } ++ } ++ ++ static int dataWithout(ChangeSubscriber prevSubscriber, ChangeSubscriber removedSubscriber, int subscriberData) { ++ return dataWithout(prevSubscriber, removedSubscriber, subscriberData, 0, false); ++ } ++ ++ static int dataWithout(ChangeSubscriber prevSubscriber, ChangeSubscriber removedSubscriber, int subscriberData, int removedSubscriberData, boolean matchData) { ++ if (prevSubscriber instanceof Multi multi) { ++ int index = multi.indexOf(removedSubscriber, removedSubscriberData, matchData); ++ if (index != -1) { ++ if (multi.subscribers.size() == 2) { ++ return multi.subscriberDatas.getInt(1 - index); ++ } else { ++ return subscriberData; ++ } ++ } else { ++ return subscriberData; ++ } ++ } ++ return prevSubscriber == removedSubscriber ? 0 : subscriberData; ++ } ++ ++ static int dataOf(ChangeSubscriber subscribers, ChangeSubscriber subscriber, int subscriberData) { ++ return subscribers instanceof Multi multi ? multi.subscriberDatas.getInt(multi.subscribers.indexOf(subscriber)) : subscriberData; ++ } ++ ++ static boolean containsSubscriber(ChangeSubscriber subscriber, int subscriberData, ChangeSubscriber subscriber1, int subscriberData1) { ++ if (subscriber instanceof Multi multi) { ++ return multi.indexOf(subscriber1, subscriberData1, true) != -1; ++ } ++ return subscriber == subscriber1 && subscriberData == subscriberData1; ++ } ++ ++ ++ /** ++ * Notify the subscriber that the publisher will be changed immediately after this call. ++ * @param publisher The publisher that is about to change ++ * @param subscriberData The data associated with the subscriber, given when the subscriber was added ++ */ ++ void notify(@Nullable T publisher, int subscriberData); ++ ++ /** ++ * Notify the subscriber about being unsubscribed from the publisher. Used when the publisher becomes invalid. ++ * The subscriber should not attempt to unsubscribe itself from the publisher in this method. ++ * ++ * @param publisher The publisher unsubscribed from ++ * @param subscriberData The data associated with the subscriber, given when the subscriber was added ++ */ ++ void forceUnsubscribe(T publisher, int subscriberData); ++ ++ interface CountChangeSubscriber extends ChangeSubscriber { ++ ++ /** ++ * Notify the subscriber that the publisher's count data will be changed immediately after this call. ++ * @param publisher The publisher that is about to change ++ * @param subscriberData The data associated with the subscriber, given when the subscriber was added ++ * @param newCount The new count of the publisher ++ */ ++ void notifyCount(T publisher, int subscriberData, int newCount); ++ } ++ ++ interface EnchantmentSubscriber extends ChangeSubscriber { ++ ++ /** ++ * Notify the subscriber that the publisher's enchantment data has been changed immediately before this call. ++ * @param publisher The publisher that has changed ++ * @param subscriberData The data associated with the subscriber, given when the subscriber was added ++ */ ++ void notifyAfterEnchantmentChange(T publisher, int subscriberData); ++ } ++ ++ class Multi implements CountChangeSubscriber, EnchantmentSubscriber { ++ private final ArrayList> subscribers; ++ private final IntArrayList subscriberDatas; ++ ++ public Multi(ArrayList> subscribers, IntArrayList subscriberDatas) { ++ this.subscribers = subscribers; ++ this.subscriberDatas = subscriberDatas; ++ } ++ ++ @Override ++ public void notify(T publisher, int subscriberData) { ++ ArrayList> changeSubscribers = this.subscribers; ++ for (int i = 0; i < changeSubscribers.size(); i++) { ++ ChangeSubscriber subscriber = changeSubscribers.get(i); ++ subscriber.notify(publisher, this.subscriberDatas.getInt(i)); ++ } ++ } ++ ++ @Override ++ public void forceUnsubscribe(T publisher, int subscriberData) { ++ ArrayList> changeSubscribers = this.subscribers; ++ for (int i = 0; i < changeSubscribers.size(); i++) { ++ ChangeSubscriber subscriber = changeSubscribers.get(i); ++ subscriber.forceUnsubscribe(publisher, this.subscriberDatas.getInt(i)); ++ } ++ } ++ ++ @Override ++ public void notifyCount(T publisher, int subscriberData, int newCount) { ++ ArrayList> changeSubscribers = this.subscribers; ++ for (int i = 0; i < changeSubscribers.size(); i++) { ++ ChangeSubscriber subscriber = changeSubscribers.get(i); ++ if (subscriber instanceof ChangeSubscriber.CountChangeSubscriber countChangeSubscriber) { ++ countChangeSubscriber.notifyCount(publisher, this.subscriberDatas.getInt(i), newCount); ++ } ++ } ++ } ++ ++ int indexOf(ChangeSubscriber subscriber, int subscriberData, boolean matchData) { ++ if (!matchData) { ++ return this.subscribers.indexOf(subscriber); ++ } else { ++ for (int i = 0; i < this.subscribers.size(); i++) { ++ if (this.subscribers.get(i) == subscriber && this.subscriberDatas.getInt(i) == subscriberData) { ++ return i; ++ } ++ } ++ return -1; ++ } ++ } ++ ++ @Override ++ public void notifyAfterEnchantmentChange(T publisher, int subscriberData) { ++ ArrayList> changeSubscribers = this.subscribers; ++ for (int i = 0; i < changeSubscribers.size(); i++) { ++ ChangeSubscriber subscriber = changeSubscribers.get(i); ++ if (subscriber instanceof ChangeSubscriber.EnchantmentSubscriber enchantmentSubscriber) { ++ enchantmentSubscriber.notifyAfterEnchantmentChange(publisher, this.subscriberDatas.getInt(i)); ++ } ++ } ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/minecraft/core/component/PatchedDataComponentMap.java b/src/main/java/net/minecraft/core/component/PatchedDataComponentMap.java +index 22da75d8197de29a150c9eade7994deecae53a10..aa8a5938984d6860deb67a36f85c83d96057d753 100644 +--- a/src/main/java/net/minecraft/core/component/PatchedDataComponentMap.java ++++ b/src/main/java/net/minecraft/core/component/PatchedDataComponentMap.java +@@ -14,10 +14,11 @@ import java.util.Map.Entry; + import java.util.stream.Collectors; + import javax.annotation.Nullable; + +-public final class PatchedDataComponentMap implements DataComponentMap { ++public final class PatchedDataComponentMap implements DataComponentMap, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher { // Leaf - Lithium equipment tracking + private final DataComponentMap prototype; + private Reference2ObjectMap, Optional> patch; + private boolean copyOnWrite; ++ private net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber; // Leaf - Lithium equipment tracking + + public PatchedDataComponentMap(DataComponentMap baseComponents) { + this(baseComponents, Reference2ObjectMaps.emptyMap(), true); +@@ -128,6 +129,9 @@ public final class PatchedDataComponentMap implements DataComponentMap { + } + + private void ensureMapOwnership() { ++ if (this.subscriber != null) { ++ this.subscriber.notify(this, 0); ++ } + if (this.copyOnWrite) { + this.patch = new Reference2ObjectArrayMap<>(this.patch); + this.copyOnWrite = false; +@@ -210,6 +214,22 @@ public final class PatchedDataComponentMap implements DataComponentMap { + return new PatchedDataComponentMap(this.prototype, this.patch, true); + } + ++ // Leaf start - Lithium equipment tracking ++ @Override ++ public void subscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { ++ if (subscriberData != 0) { ++ throw new UnsupportedOperationException("ComponentMapImpl does not support subscriber data"); ++ } ++ this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.combine(this.subscriber, 0, subscriber, 0); ++ } ++ ++ @Override ++ public int unsubscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber) { ++ this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber); ++ return 0; ++ } ++ // Leaf end - Lithium equipment tracking ++ + @Override + public boolean equals(Object object) { + if (this == object) { +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 9de85972ba32fd2373f70f708aa1bfc6067e6e1c..3ae75230a4250665df7a72aff1d84317b81fc077 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -157,7 +157,7 @@ import org.bukkit.event.entity.EntityTeleportEvent; + import org.bukkit.event.player.PlayerItemConsumeEvent; + // CraftBukkit end + +-public abstract class LivingEntity extends Entity implements Attackable { ++public abstract class LivingEntity extends Entity implements Attackable, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber, net.caffeinemc.mods.lithium.common.entity.EquipmentEntity, net.caffeinemc.mods.lithium.common.entity.EquipmentEntity.TickableEnchantmentTrackingEntity, net.caffeinemc.mods.lithium.common.entity.EquipmentEntity.EquipmentTrackingEntity { // Leaf - Lithium equipment tracking + + private static final Logger LOGGER = LogUtils.getLogger(); + private static final String TAG_ACTIVE_EFFECTS = "active_effects"; +@@ -290,6 +290,10 @@ public abstract class LivingEntity extends Entity implements Attackable { + public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API + protected boolean shouldBurnInDay = false; public boolean shouldBurnInDay() { return this.shouldBurnInDay; } public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } // Purpur - API for any mob to burn daylight + ++ // Leaf start - Lithium equipment tracking ++ private boolean maybeHasTickableEnchantments = this instanceof net.minecraft.world.entity.player.Player; ++ private boolean equipmentChanged = true; ++ // Leaf end - Lithium equipment tracking + @Override + public float getBukkitYaw() { + return this.getYHeadRot(); +@@ -442,7 +446,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + + Level world = this.level(); + +- if (world instanceof ServerLevel worldserver) { ++ if (this.maybeHasTickableEnchantments && world instanceof ServerLevel worldserver) { // Leaf - Lithium equipment tracking + EnchantmentHelper.tickEffects(worldserver, this); + } + +@@ -735,6 +739,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + boolean flag = itemstack1.isEmpty() && itemstack.isEmpty(); + + if (!flag && !ItemStack.isSameItemSameComponents(itemstack, itemstack1) && !this.firstTick) { ++ this.onEquipmentReplaced(itemstack, itemstack1); // Leaf - Lithium equipment tracking + Equipable equipable = Equipable.get(itemstack1); + + if (!this.level().isClientSide() && !this.isSpectator()) { +@@ -3382,6 +3387,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + Map map = this.collectEquipmentChanges(); + + if (map != null) { ++ if (!(this instanceof net.minecraft.world.entity.player.Player)) this.equipmentChanged = false; // Leaf - Lithium equipment tracking + this.handleHandSwap(map); + if (!map.isEmpty()) { + this.handleEquipmentChanges(map); +@@ -3392,6 +3398,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + + @Nullable + private Map collectEquipmentChanges() { ++ if (!this.equipmentChanged) return null; // Leaf - Lithium equipment tracking + Map map = null; + EquipmentSlot[] aenumitemslot = EquipmentSlot.VALUES; // Gale - JettPack - reduce array allocations + int i = aenumitemslot.length; +@@ -4848,6 +4855,80 @@ public abstract class LivingEntity extends Entity implements Attackable { + flag = true; + return flag; + } ++ // Leaf start - Lithium entity equipment tracking ++ @Override ++ public void updateHasTickableEnchantments(ItemStack oldStack, ItemStack newStack) { ++ if (!this.maybeHasTickableEnchantments) { ++ this.maybeHasTickableEnchantments = stackHasTickableEnchantment(newStack); ++ } ++ } ++ ++ @Override ++ public void notifyAfterEnchantmentChange(ItemStack publisher, int subscriberData) { ++ if (!this.maybeHasTickableEnchantments) { ++ this.maybeHasTickableEnchantments = stackHasTickableEnchantment(publisher); ++ } ++ } ++ ++ @Override ++ public void onEquipmentChanged() { ++ this.equipmentChanged = true; ++ } ++ ++ private static boolean stackHasTickableEnchantment(ItemStack stack) { ++ if (!stack.isEmpty()) { ++ net.minecraft.world.item.enchantment.ItemEnchantments enchantments = stack.get(DataComponents.ENCHANTMENTS); ++ if (enchantments != null && !enchantments.isEmpty()) { ++ for (Holder enchantmentEntry : enchantments.keySet()) { ++ if (!enchantmentEntry.value().getEffects(net.minecraft.world.item.enchantment.EnchantmentEffectComponents.TICK).isEmpty()) { ++ return true; ++ } ++ } ++ return false; ++ } ++ } ++ return false; ++ } ++ @Override ++ public void notify(@Nullable ItemStack publisher, int zero) { ++ if (this instanceof EquipmentTrackingEntity equipmentTrackingEntity) { ++ equipmentTrackingEntity.onEquipmentChanged(); ++ } ++ } ++ ++ @Override ++ public void notifyCount(ItemStack publisher, int zero, int newCount) { ++ if (newCount == 0) { ++ //noinspection unchecked ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) (Object) publisher).unsubscribeWithData(this, zero); ++ } ++ ++ this.onEquipmentReplaced(publisher, ItemStack.EMPTY); ++ } ++ ++ @Override ++ public void forceUnsubscribe(ItemStack publisher, int zero) { ++ throw new UnsupportedOperationException(); ++ } ++ ++ @Override ++ public void onEquipmentReplaced(ItemStack oldStack, ItemStack newStack) { ++ if (this instanceof TickableEnchantmentTrackingEntity enchantmentTrackingEntity) { ++ enchantmentTrackingEntity.updateHasTickableEnchantments(oldStack, newStack); ++ } ++ ++ if (this instanceof EquipmentTrackingEntity equipmentTrackingEntity) { ++ equipmentTrackingEntity.onEquipmentChanged(); ++ } ++ ++ if (!oldStack.isEmpty()) { ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) oldStack).unsubscribeWithData(this, 0); ++ } ++ if (!newStack.isEmpty()) { ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) newStack).subscribe(this, 0); ++ } ++ } ++ // Leaf end - Lithium entity equipment tracking + + public static record Fallsounds(SoundEvent small, SoundEvent big) { + +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 9d196c8a8a0dc49a54264471429b6ff6da8c2b06..2ba607df6ee0500a7ac51cefd076840ae35e4827 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -97,7 +97,7 @@ import org.bukkit.event.entity.EntityUnleashEvent; + import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; + // CraftBukkit end + +-public abstract class Mob extends LivingEntity implements EquipmentUser, Leashable, Targeting { ++public abstract class Mob extends LivingEntity implements EquipmentUser, Leashable, Targeting, net.caffeinemc.mods.lithium.common.entity.EquipmentEntity { // Leaf - Lithium equipment tracking + + private static final EntityDataAccessor DATA_MOB_FLAGS_ID = SynchedEntityData.defineId(Mob.class, EntityDataSerializers.BYTE); + private static final int MOB_FLAG_NO_AI = 1; +@@ -583,6 +583,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + + @Override + public void readAdditionalSaveData(CompoundTag nbt) { ++ ItemStack prevBodyArmor = this.bodyArmorItem; // Leaf - Lithium equipment tracking + super.readAdditionalSaveData(nbt); + + // CraftBukkit start - If looting or persistence is false only use it if it was set after we started using it +@@ -607,7 +608,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + + for (i = 0; i < this.armorItems.size(); ++i) { + nbttagcompound1 = nbttaglist.getCompound(i); +- this.armorItems.set(i, ItemStack.parseOptional(this.registryAccess(), nbttagcompound1)); ++ // Leaf start - Lithium equipment tracking ++ ItemStack currStack = ItemStack.parseOptional(this.registryAccess(), nbttagcompound1); ++ ItemStack prevStack = this.armorItems.set(i, currStack); ++ this.trackEquipChange(prevStack, currStack); ++ // Leaf end - Lithium equipment tracking + } + } + +@@ -624,7 +629,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + + for (i = 0; i < this.handItems.size(); ++i) { + nbttagcompound1 = nbttaglist.getCompound(i); +- this.handItems.set(i, ItemStack.parseOptional(this.registryAccess(), nbttagcompound1)); ++ // Leaf start - Lithium equipment tracking ++ ItemStack currStack = ItemStack.parseOptional(this.registryAccess(), nbttagcompound1); ++ ItemStack prevStack = this.handItems.set(i, currStack); ++ this.trackEquipChange(prevStack, currStack); ++ // Leaf end - Lithium equipment tracking + } + } + +@@ -661,6 +670,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + this.ticksSinceLastInteraction = nbt.getInt("Purpur.ticksSinceLastInteraction"); + } + // Purpur end ++ // Leaf start - Lithium equipment tracking ++ if (prevBodyArmor != this.bodyArmorItem) { ++ this.trackEquipChange(prevBodyArmor, this.bodyArmorItem); ++ } ++ // Leaf end - Lithium equipment tracking + } + + @Override +@@ -1865,4 +1879,10 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + } + } + // Purpur end ++ ++ // Leaf start - Lithium equipment tracking ++ private void trackEquipChange(ItemStack prevStack, ItemStack currStack) { ++ this.onEquipmentReplaced(prevStack, currStack); ++ } ++ // Leaf end - Lithium equipment tracking + } +diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java +index 3bb46ed871fd56bbbe52cfd2575f9e853e03cd73..6d73ae2a453feeae0aea67bb555c64a0a288c376 100644 +--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java ++++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java +@@ -52,7 +52,7 @@ import org.bukkit.entity.Player; + import org.bukkit.event.player.PlayerArmorStandManipulateEvent; + // CraftBukkit end + +-public class ArmorStand extends LivingEntity { ++public class ArmorStand extends LivingEntity implements net.caffeinemc.mods.lithium.common.entity.EquipmentEntity { // Leaf - Lithium equipment tracking + + public static final int WOBBLE_TIME = 5; + private static final boolean ENABLE_ARMS = true; +@@ -269,7 +269,11 @@ public class ArmorStand extends LivingEntity { + + for (i = 0; i < this.armorItems.size(); ++i) { + nbttagcompound1 = nbttaglist.getCompound(i); +- this.armorItems.set(i, ItemStack.parseOptional(this.registryAccess(), nbttagcompound1)); ++ // Leaf start - Lithium equipment tracking ++ ItemStack currElement = ItemStack.parseOptional(this.registryAccess(), nbttagcompound1); ++ ItemStack prevElement = this.armorItems.set(i, currElement); ++ this.trackEquipChange(prevElement, currElement); ++ // Leaf end - Lithium equipment tracking + } + } + +@@ -278,7 +282,11 @@ public class ArmorStand extends LivingEntity { + + for (i = 0; i < this.handItems.size(); ++i) { + nbttagcompound1 = nbttaglist.getCompound(i); +- this.handItems.set(i, ItemStack.parseOptional(this.registryAccess(), nbttagcompound1)); ++ // Leaf start - Lithium equipment tracking ++ ItemStack currStack = ItemStack.parseOptional(this.registryAccess(), nbttagcompound1); ++ ItemStack prevStack = this.handItems.set(i, currStack); ++ this.trackEquipChange(prevStack, currStack); ++ // Leaf end - Lithium equipment tracking + } + } + +@@ -640,7 +648,11 @@ public class ArmorStand extends LivingEntity { + itemstack = (ItemStack) this.handItems.get(i); + if (!itemstack.isEmpty()) { + this.drops.add(new DefaultDrop(itemstack, stack -> Block.popResource(this.level(), this.blockPosition().above(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior; mirror so we can destroy it later - though this call site was safe & spawn drops correctly +- this.handItems.set(i, ItemStack.EMPTY); ++ // Leaf start - Lithium equipment tracking ++ ItemStack emptyStack = ItemStack.EMPTY; ++ ItemStack prevStack = this.handItems.set(i, emptyStack); ++ this.trackEquipChange(prevStack, emptyStack); ++ // Leaf end - Lithium equipment tracking + } + } + +@@ -648,7 +660,11 @@ public class ArmorStand extends LivingEntity { + itemstack = (ItemStack) this.armorItems.get(i); + if (!itemstack.isEmpty()) { + this.drops.add(new DefaultDrop(itemstack, stack -> Block.popResource(this.level(), this.blockPosition().above(), stack))); // CraftBukkit - add to drops // Paper - Restore vanilla drops behavior; mirror so we can destroy it later - though this call site was safe & spawn drops correctly +- this.armorItems.set(i, ItemStack.EMPTY); ++ // Leaf start - Lithium equipment tracking ++ ItemStack emptyStack = ItemStack.EMPTY; ++ ItemStack prevStack = this.armorItems.set(i, ItemStack.EMPTY); ++ this.trackEquipChange(prevStack, emptyStack); ++ // Leaf end - Lithium equipment tracking + } + } + return this.dropAllDeathLoot(world, damageSource); // CraftBukkit - moved from above // Paper +@@ -1034,4 +1050,10 @@ public class ArmorStand extends LivingEntity { + if (this.canMovementTick && this.canMove) super.aiStep(); + } + // Purpur end ++ ++ // Leaf start - Lithium equipment tracking ++ private void trackEquipChange(ItemStack prevStack, ItemStack currStack) { ++ this.onEquipmentReplaced(prevStack, currStack); ++ } ++ // Leaf end - Lithium equipment tracking + } +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index 933b7519da5330ea8acd05c337201f52cab12c3c..b4fc8fcfb7994705a4307d9b0731bcd55f81d1fd 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -120,7 +120,7 @@ import org.bukkit.event.player.PlayerItemDamageEvent; + import org.bukkit.event.world.StructureGrowEvent; + // CraftBukkit end + +-public final class ItemStack implements DataComponentHolder { ++public final class ItemStack implements DataComponentHolder, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber { // Leaf - Lithium equipment tracking + + public static final Codec> ITEM_NON_AIR_CODEC = BuiltInRegistries.ITEM.holderByNameCodec().validate((holder) -> { + return holder.is((Holder) Items.AIR.builtInRegistryHolder()) ? DataResult.error(() -> { +@@ -228,6 +228,11 @@ public final class ItemStack implements DataComponentHolder { + private PatchedDataComponentMap components; + @Nullable + private Entity entityRepresentation; ++ // Leaf start - Lithium equipment tracking ++ @Nullable ++ private net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber; ++ private int subscriberData; ++ // Leaf end - Lithium equipment tracking + + private static DataResult validateStrict(ItemStack stack) { + DataResult dataresult = ItemStack.validateComponents(stack.getComponents()); +@@ -1368,6 +1373,23 @@ public final class ItemStack implements DataComponentHolder { + } + + public void setCount(int count) { ++ // Leaf start - Lithium equipment tracking ++ if (count != this.count) { ++ if (this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber countChangeSubscriber) { ++ countChangeSubscriber.notifyCount(this, this.subscriberData, count); ++ } ++ if (count == 0) { ++ //Safe because ComponentMapImplMixin implements the interface ++ //noinspection unchecked ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) (Object) this.components).unsubscribe(this); ++ if (this.subscriber != null) { ++ this.subscriber.forceUnsubscribe(this, this.subscriberData); ++ this.subscriber = null; ++ this.subscriberData = 0; ++ } ++ } ++ } ++ // Leaf end - Lithium equipment tracking + this.count = count; + } + +@@ -1423,4 +1445,91 @@ public final class ItemStack implements DataComponentHolder { + public boolean canBeHurtBy(DamageSource source) { + return !this.has(DataComponents.FIRE_RESISTANT) || !source.is(DamageTypeTags.IS_FIRE); + } ++ ++ // Leaf start - Lithium equipment tracking ++ @Override ++ public void subscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { ++ if (this.isEmpty()) { ++ throw new IllegalStateException("Cannot subscribe to an empty ItemStack!"); ++ } ++ ++ if (this.subscriber == null) { ++ this.startTrackingChanges(); ++ } ++ this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.combine(this.subscriber, this.subscriberData, subscriber, subscriberData); ++ if (this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.Multi) { ++ this.subscriberData = 0; ++ } else { ++ this.subscriberData = subscriberData; ++ } ++ } ++ ++ @Override ++ public int unsubscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber) { ++ if (this.isEmpty()) { ++ throw new IllegalStateException("Cannot unsubscribe from an empty ItemStack!"); ++ } ++ ++ int retval = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataOf(this.subscriber, subscriber, this.subscriberData); ++ this.subscriberData = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData); ++ this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber); ++ ++ if (this.subscriber == null) { ++ //noinspection unchecked ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) (Object) this.components).unsubscribe(this); ++ } ++ return retval; ++ } ++ ++ @Override ++ public void unsubscribeWithData(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { ++ if (this.isEmpty()) { ++ throw new IllegalStateException("Cannot unsubscribe from an empty ItemStack!"); ++ } ++ ++ this.subscriberData = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData, subscriberData, true); ++ this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber, subscriberData, true); ++ ++ if (this.subscriber == null) { ++ //noinspection unchecked ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) (Object) this.components).unsubscribe(this); ++ } ++ } ++ ++ @Override ++ public boolean isSubscribedWithData(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { ++ if (this.isEmpty()) { ++ throw new IllegalStateException("Cannot be subscribed to an empty ItemStack!"); ++ } ++ ++ return net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.containsSubscriber(this.subscriber, this.subscriberData, subscriber, subscriberData); ++ } ++ ++ @Override ++ public void forceUnsubscribe(PatchedDataComponentMap publisher, int subscriberData) { ++ if (publisher != this.components) { ++ throw new IllegalStateException("Invalid publisher, expected " + this.components + " but got " + publisher); ++ } ++ this.subscriber.forceUnsubscribe(this, this.subscriberData); ++ this.subscriber = null; ++ this.subscriberData = 0; ++ } ++ ++ @Override ++ public void notify(PatchedDataComponentMap publisher, int subscriberData) { ++ if (publisher != this.components) { ++ throw new IllegalStateException("Invalid publisher, expected " + this.components + " but got " + publisher); ++ } ++ ++ if (this.subscriber != null) { ++ this.subscriber.notify(this, this.subscriberData); ++ } ++ } ++ ++ private void startTrackingChanges() { ++ //Safe because ComponentMapImplMixin ++ //noinspection unchecked ++ ((net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher) (Object) this.components).subscribe(this, 0); ++ } ++ // Leaf end - Lithium equipment tracking + } diff --git a/patches/server/0153-Faster-CraftServer-getworlds-list-creation.patch b/patches/server/0153-Faster-CraftServer-getworlds-list-creation.patch new file mode 100644 index 000000000..d5751a759 --- /dev/null +++ b/patches/server/0153-Faster-CraftServer-getworlds-list-creation.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> +Date: Tue, 9 Nov 2077 00:00:00 +0800 +Subject: [PATCH] Faster CraftServer#getworlds list creation + +CraftServer#getWorlds/Bukkit#getWorlds is frequently used in plugins, +replacing ArrayList with Fastutil ObjectArrayList +brings about 40% performance improvement in benchmark. + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 5f4e5b386047258948a0c8772d93bfa8be734ffc..c7d806f7e2ddef2226be1efbe794f0da4c331615 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -976,7 +976,7 @@ public final class CraftServer implements Server { + + @Override + public List getWorlds() { +- return new ArrayList(this.worlds.values()); ++ return new it.unimi.dsi.fastutil.objects.ObjectArrayList(this.worlds.values()); // Leaf - Faster CraftServer#getWorlds list creation + } + + @Override