From 8fa0233d4bde3aa3a8b7a69a5dfdefdb79149d23 Mon Sep 17 00:00:00 2001 From: snoopdoooggyttv <82676057+snoopdoooggyttv@users.noreply.github.com> Date: Fri, 16 Apr 2021 19:43:28 +0100 Subject: [PATCH 1/4] [CI-SKIP] Fix typo! (#462) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2d1f6158..fcd886b5 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ ## License ## Security -Security information can be found found [here](/SECURITY.md). +Security information can be found [here](/SECURITY.md). ## Statistics [![bStats Graph Data](https://bstats.org/signatures/server-implementation/Yatopia.svg)](https://bstats.org/plugin/server-implementation/Yatopia) From 08b5fda04ea1e3d4b42073f39a3729db342c9f66 Mon Sep 17 00:00:00 2001 From: snoopdoooggyttv <82676057+snoopdoooggyttv@users.noreply.github.com> Date: Fri, 16 Apr 2021 20:09:41 +0100 Subject: [PATCH 2/4] [ci-skip] Other typo fix (#463) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fcd886b5..3292da35 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ ## Why aren't there many API additions? (Modified from [starlis/empirecraft](https://github.com/starlis/empirecraft/))

-APIs are tough to design. In projects such as Bukkit, Spigot, Sponge, Paper, etc once an API is commited, it's almost forever. You can't go breaking it without solid justification. This is the politics game. +APIs are tough to design. In projects such as Bukkit, Spigot, Sponge, Paper, etc once an API is committed, it's almost forever. You can't go breaking it without solid justification. This is the politics game. With that in mind, much thought has to be given to the API in now and future use cases and applications to ensure it can be extended without breaking. From d99cf54b0cad23faba412576333a9c14d2886ef4 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Sat, 17 Apr 2021 20:13:30 -0400 Subject: [PATCH 3/4] Merge Staging (#458) * on the latest version now * MEGA port PR (#459) * add all of the patches * cleanup patches * update README.md and LICENSE.md * forgot about tic-tacs! * ...and AI-Improvements * give JellySquid credit for lithium-gen.patch * drop AI-improvements * rebuild * [CI-SKIP] co-author Hugo for some patches --- Licensing/LICENSE.md | 3 + PATCHES.md | 11 + README.md | 3 + buildSrc/src/main/kotlin/MCDevImports.kt | 21 +- patches/server/0060-Port-Cadmium.patch | 1248 ++++++++++ patches/server/0061-Port-LazyDFU.patch | 49 + .../server/0062-Lithium-CompactSineLUT.patch | 138 ++ patches/server/0063-tic-tacs-unblocking.patch | 407 ++++ ...-BlockPos.iterateOutwards-by-caching.patch | 196 ++ patches/server/0065-lithium-AI.patch | 2154 +++++++++++++++++ patches/server/0066-lithium-block.patch | 232 ++ patches/server/0067-lithium-entity.patch | 60 + patches/server/0068-lithium-gen.patch | 40 + patches/server/0069-lithium-shape.patch | 358 +++ ...king-block-entities-that-are-doing-n.patch | 1218 ++++++++++ 15 files changed, 6136 insertions(+), 2 deletions(-) create mode 100644 patches/server/0060-Port-Cadmium.patch create mode 100644 patches/server/0061-Port-LazyDFU.patch create mode 100644 patches/server/0062-Lithium-CompactSineLUT.patch create mode 100644 patches/server/0063-tic-tacs-unblocking.patch create mode 100644 patches/server/0064-lithium-optimize-BlockPos.iterateOutwards-by-caching.patch create mode 100644 patches/server/0065-lithium-AI.patch create mode 100644 patches/server/0066-lithium-block.patch create mode 100644 patches/server/0067-lithium-entity.patch create mode 100644 patches/server/0068-lithium-gen.patch create mode 100644 patches/server/0069-lithium-shape.patch create mode 100644 patches/server/0070-lithium-skip-ticking-block-entities-that-are-doing-n.patch diff --git a/Licensing/LICENSE.md b/Licensing/LICENSE.md index e481974e..2d768ddd 100644 --- a/Licensing/LICENSE.md +++ b/Licensing/LICENSE.md @@ -3,5 +3,8 @@ All patches (.patch files) marked with "lithium" are licensed under LGPL3 found [here](https://github.com/CaffeineMC/lithium-fabric/blob/1.16.x/dev/LICENSE.txt).
All patches (.patch files) marked with "hydrogen" are licensed under LGPL3 found [here](https://github.com/CaffeineMC/hydrogen-fabric/blob/1.16.x/LICENSE.txt).
All patches (.patch files) marked with "krypton" are licensed under MIT found [here](https://github.com/astei/krypton/blob/master/LICENSE).
+All patches (.patch files) marked with "Cadmium" are licensed under MIT found [here](https://github.com/LucilleTea/cadmium-fabric/blob/1.16.x/dev-my-fork/LICENSE.txt).
+All patches (.patch files) marked with "LazyDFU" are licensed under MIT found [here](https://github.com/astei/lazydfu/blob/master/LICENSE).
+All patches (.patch files) marked with "Tic-Tacs" are licensed under LGPLv3 found [here](https://github.com/Gegy/tic-tacs/blob/1.16.4/LICENSE).
All other patches (.patch files) included in this repo are licensed under the MIT license found [here](MIT.md).
See [EMC](https://github.com/starlis/empirecraft/blob/master/README.md), [Akarin](https://github.com/Akarin-project/Akarin/blob/1.16.5/LICENSE.md), [Purpur](https://github.com/pl3xgaming/Purpur/blob/ver/1.16.5/LICENSE), [Airplane](https://github.com/Technove/Airplane/blob/master/PATCHES-LICENSE), [Origami](https://github.com/Minebench/Origami/blob/1.16/PATCHES-LICENSE), and [Tuinity](https://github.com/Spottedleaf/Tuinity/blob/master/PATCHES-LICENSE) for the license of patches automatically pulled during upstream updates. diff --git a/PATCHES.md b/PATCHES.md index 039704d7..9de5091c 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -252,6 +252,7 @@ # Patches | api | Lagging threshold | William Blake Galbreath | | | server | Left handed API | BillyGalbreath | | | api | Left handed API | BillyGalbreath | | +| server | Lithium: CompactSineLUT | JellySquid | | | server | LivingEntity safeFallDistance | William Blake Galbreath | | | api | LivingEntity safeFallDistance | William Blake Galbreath | | | server | LivingEntity#broadcastItemBreak | William Blake Galbreath | | @@ -332,6 +333,8 @@ # Patches | api | PlayerSetSpawnerTypeWithEggEvent | William Blake Galbreath | | | server | Players should not cram to death | William Blake Galbreath | | | server | Populator seed controls | Spottedleaf | | +| server | Port Cadmium | Lucy-t | | +| server | Port LazyDFU | Andrew Steinborn | | | server | Preload ProtocolLib EnumWrappers | ishland | | | server | Prevent long map entry creation in light engine | Spottedleaf | | | server | Prevent unload() calls removing tickets for sync loads | Spottedleaf | | @@ -422,6 +425,7 @@ # Patches | server | Zombie horse naturally spawn | William Blake Galbreath | | | server | add config for logging login location | Simon Gardling | | | server | dont load chunks for physics | Aikar | | +| server | lithium AI | JellySquid | | | server | lithium DataTrackerMixin | JellySquid | tr7zw | | server | lithium HashedList | JellySquid | | | server | lithium MixinBox | JellySquid | | @@ -430,6 +434,13 @@ # Patches | server | lithium NoiseChunkGeneratorMixin | JellySquid | | | server | lithium PerlinNoiseSamplerMixin | JellySquid | Bud Gidiere | | server | lithium VoronoiBiomeAccessTypeMixin | JellySquid | | +| server | lithium block | JellySquid | | +| server | lithium entity | JellySquid | | | server | lithium enum_values | JellySquid | | +| server | lithium gen | JellySquid | | | server | lithium reduce allocations | JellySquid | Mykyta Komarnytskyy | +| server | lithium shape | JellySquid | | | server | lithium: cache chunk gen sea level | SuperCoder7979 | | +| server | lithium: optimize `BlockPos.iterateOutwards` by caching | 2No2Name | | +| server | lithium: skip ticking block entities that are doing nothing | 2No2Name | | +| server | tic-tacs: unblocking | Gegy | | diff --git a/README.md b/README.md index 3292da35..a1ae85f9 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ ## So what is Yatopia? * [Origami](https://github.com/Minebench/Origami) * [Purpur](https://github.com/pl3xgaming/Purpur) * [Airplane](https://github.com/Technove/Airplane) +* [Cadmium](https://github.com/LucilleTea/cadmium-fabric) +* [LazyDFU](https://github.com/astei/lazydfu) +* [Tic-Tacs](https://github.com/Gegy/tic-tacs) ## Try it out diff --git a/buildSrc/src/main/kotlin/MCDevImports.kt b/buildSrc/src/main/kotlin/MCDevImports.kt index a2c1d481..d58be4f5 100644 --- a/buildSrc/src/main/kotlin/MCDevImports.kt +++ b/buildSrc/src/main/kotlin/MCDevImports.kt @@ -24,5 +24,22 @@ val libraryImports = setOf( LibraryImport("com.mojang", "brigadier", "com/mojang/brigadier", "CommandDispatcher"), LibraryImport("com.mojang", "brigadier", "com/mojang/brigadier/tree", "LiteralCommandNode"), LibraryImport("com.mojang", "brigadier", "com/mojang/brigadier/suggestion", "SuggestionsBuilder"), - LibraryImport("com.mojang", "brigadier", "com/mojang/brigadier/arguments", "BoolArgumentType") -) + LibraryImport("com.mojang", "brigadier", "com/mojang/brigadier/arguments", "BoolArgumentType"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers", "FieldFinder"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers", "DataFixUtils"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers", "TypeRewriteRule"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers", "Typed"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers", "TypedOptic"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers", "View"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/functions", "Apply"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/functions", "Comp"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/functions", "PointFree"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/functions", "PointFreeRule"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/optics", "IdAdapter"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/optics", "Inj1"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/optics", "Inj2"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/optics", "Optics"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/optics", "Proj1"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/optics", "Proj2"), + LibraryImport("com.mojang", "datafixerupper", "com/mojang/datafixers/types", "Type") +) \ No newline at end of file diff --git a/patches/server/0060-Port-Cadmium.patch b/patches/server/0060-Port-Cadmium.patch new file mode 100644 index 00000000..3ef5f8a1 --- /dev/null +++ b/patches/server/0060-Port-Cadmium.patch @@ -0,0 +1,1248 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Lucy-t <64509677+Lucy-t@users.noreply.github.com> +Date: Thu, 7 Jan 2021 00:24:16 -0500 +Subject: [PATCH] Port Cadmium + +Code from LucilleTea's port of cadmium to 1.16: https://github.com/LucilleTea/DataFixerUpper + +diff --git a/src/main/java/com/mojang/datafixers/DataFixUtils.java b/src/main/java/com/mojang/datafixers/DataFixUtils.java +index cf8959aea3e48c1276ae9536d4de0f0127e1801e..b730a4c0b8b8c9036641d033568b3554062999cb 100644 +--- a/src/main/java/com/mojang/datafixers/DataFixUtils.java ++++ b/src/main/java/com/mojang/datafixers/DataFixUtils.java +@@ -54,6 +54,15 @@ public class DataFixUtils { + return other; + } + ++ // Yatopia start - Cadmium port ++ public static U orElse(final U nullable, final U other) { ++ if (nullable!=null) { ++ return nullable; ++ } ++ return other; ++ } ++ // Yatopia end ++ + public static U orElseGet(final Optional optional, final Supplier other) { + if (optional.isPresent()) { + return optional.get(); +diff --git a/src/main/java/com/mojang/datafixers/DataFixerBuilder.java b/src/main/java/com/mojang/datafixers/DataFixerBuilder.java +index abc265b00044b14abb55c2628d454ee01fef467b..c8c19535fa64edc392fa18093ba47b16a168ebaf 100644 +--- a/src/main/java/com/mojang/datafixers/DataFixerBuilder.java ++++ b/src/main/java/com/mojang/datafixers/DataFixerBuilder.java +@@ -64,13 +64,17 @@ public class DataFixerBuilder { + public DataFixer build(final Executor executor) { + final DataFixerUpper fixerUpper = new DataFixerUpper(new Int2ObjectAVLTreeMap<>(schemas), new ArrayList<>(globalList), new IntAVLTreeSet(fixerVersions)); + ++ // Yatopia start - Cadmium port ++ long startTime = System.nanoTime(); ++ final List> futures = Lists.newArrayList(); ++ // Yatopia end + final IntBidirectionalIterator iterator = fixerUpper.fixerVersions().iterator(); + while (iterator.hasNext()) { + final int versionKey = iterator.nextInt(); + if (versionKey < minDataFixPrecacheVersion) continue; // Paper + final Schema schema = schemas.get(versionKey); + for (final String typeName : schema.types()) { +- CompletableFuture.runAsync(() -> { ++ futures.add(CompletableFuture.runAsync(() -> { // Yatopia - Cadmium port + final Type dataType = schema.getType(() -> typeName); + final TypeRewriteRule rule = fixerUpper.getRule(DataFixUtils.getVersion(versionKey), dataVersion); + dataType.rewrite(rule, DataFixerUpper.OPTIMIZATION_RULE); +@@ -78,9 +82,16 @@ public class DataFixerBuilder { + LOGGER.error("Unable to build datafixers", e); + Runtime.getRuntime().exit(1); + return null; +- }); ++ })); // Yatopia - Cadmium port + } + } ++ // Yatopia start - Cadmium port ++ CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) ++ .thenAccept((res) -> { ++ long endTime = System.nanoTime(); ++ LOGGER.info("Finished building data fixers after {}ms", (endTime - startTime) / 1_000_000); ++ }); ++ // Yatopia end + + return fixerUpper; + } +diff --git a/src/main/java/com/mojang/datafixers/FieldFinder.java b/src/main/java/com/mojang/datafixers/FieldFinder.java +index a20a9dedcff2f3d633c5fd09d238a98fe18968c0..7ae4fab4c448ec3444624b6e671364a89243f6dc 100644 +--- a/src/main/java/com/mojang/datafixers/FieldFinder.java ++++ b/src/main/java/com/mojang/datafixers/FieldFinder.java +@@ -118,7 +118,7 @@ public final class FieldFinder implements OpticFinder { + (Type>) choiceType, + type, + type, +- new Proj1<>() ++ Proj1.instance() // Yatopia - Cadmium port + ); + } + +diff --git a/src/main/java/com/mojang/datafixers/TypeRewriteRule.java b/src/main/java/com/mojang/datafixers/TypeRewriteRule.java +index 6f836c3382ed83e07000c0a5e4b6e9b69f8c45bf..37690009f2f3782e741f71272df525e9093794bc 100644 +--- a/src/main/java/com/mojang/datafixers/TypeRewriteRule.java ++++ b/src/main/java/com/mojang/datafixers/TypeRewriteRule.java +@@ -105,20 +105,24 @@ public interface TypeRewriteRule { + } + } + +- static TypeRewriteRule orElse(final TypeRewriteRule first, final TypeRewriteRule second) { ++ // Yatopia start - Cadmium port ++ static TypeRewriteRule orElse(final TypeRewriteRule first, final TypeRewriteRule second) { ++ /* + return orElse(first, () -> second); + } + + static TypeRewriteRule orElse(final TypeRewriteRule first, final Supplier second) { +- return new OrElse(first, second); +- } ++ */ ++ return new OrElse(first, second); ++ } ++ // Yatopia end + + final class OrElse implements TypeRewriteRule { + protected final TypeRewriteRule first; +- protected final Supplier second; ++ protected final TypeRewriteRule second; // Yatopia - Cadmium port + private final int hashCode; + +- public OrElse(final TypeRewriteRule first, final Supplier second) { ++ public OrElse(final TypeRewriteRule first, final TypeRewriteRule second) { // Yatopia - Cadmium port + this.first = first; + this.second = second; + hashCode = Objects.hash(first, second); +@@ -130,7 +134,7 @@ public interface TypeRewriteRule { + if (view.isPresent()) { + return view; + } +- return second.get().rewrite(type); ++ return second.rewrite(type); // Yatopia - Cadmium port + } + + @Override +@@ -159,9 +163,13 @@ public interface TypeRewriteRule { + return new One(rule); + } + ++ // Yatopia start - Cadmium port ++ /* + static TypeRewriteRule once(final TypeRewriteRule rule) { + return orElse(rule, () -> one(once(rule))); + } ++ */ ++ // Yatopia end + + static TypeRewriteRule checkOnce(final TypeRewriteRule rule, final Consumer> onFail) { + // TODO: toggle somehow +diff --git a/src/main/java/com/mojang/datafixers/Typed.java b/src/main/java/com/mojang/datafixers/Typed.java +index 55c55beba58fb0505ccf8270a27d6c6ffc01c629..4b84d12d51eedf99d0b5d2168f31c8a2f819b432 100644 +--- a/src/main/java/com/mojang/datafixers/Typed.java ++++ b/src/main/java/com/mojang/datafixers/Typed.java +@@ -184,11 +184,11 @@ public final class Typed { + } + + public Typed> inj1(final Type type) { +- return new Typed<>(DSL.or(this.type, type), ops, new Inj1().build(value)); ++ return new Typed<>(DSL.or(this.type, type), ops, Inj1.instance().build(value)); // Yatopia - Cadmium port + } + + public Typed> inj2(final Type type) { +- return new Typed<>(DSL.or(type, this.type), ops, new Inj2().build(value)); ++ return new Typed<>(DSL.or(type, this.type), ops, Inj2.instance().build(value)); // Yatopia - Cadmium port + } + + public static Typed> pair(final Typed first, final Typed second) { +diff --git a/src/main/java/com/mojang/datafixers/TypedOptic.java b/src/main/java/com/mojang/datafixers/TypedOptic.java +index 10c5565709510609f8b6c4c39bb25b163dd878bc..9e2b6e46ad846ebd964b969932da84c671ebc651 100644 +--- a/src/main/java/com/mojang/datafixers/TypedOptic.java ++++ b/src/main/java/com/mojang/datafixers/TypedOptic.java +@@ -131,7 +131,7 @@ public final class TypedOptic { + DSL.and(newType, gType), + fType, + newType, +- new Proj1<>() ++ Proj1.instance() // Yatopia - Cadmium port + ); + } + +@@ -142,7 +142,7 @@ public final class TypedOptic { + DSL.and(fType, newType), + gType, + newType, +- new Proj2<>() ++ Proj2.instance() // Yatopia - Cadmium port + ); + } + +@@ -153,7 +153,7 @@ public final class TypedOptic { + DSL.or(newType, gType), + fType, + newType, +- new Inj1<>() ++ Inj1.instance() // Yatopia - Cadmium port + ); + } + +@@ -164,7 +164,7 @@ public final class TypedOptic { + DSL.or(fType, newType), + gType, + newType, +- new Inj2<>() ++ Inj2.instance() // Yatopia - Cadmium port + ); + } + +diff --git a/src/main/java/com/mojang/datafixers/View.java b/src/main/java/com/mojang/datafixers/View.java +index 4139dc999917d84c7dea5b02117a50af51206f04..58f856d95405f02c107fad12e830e98d1e1a8337 100644 +--- a/src/main/java/com/mojang/datafixers/View.java ++++ b/src/main/java/com/mojang/datafixers/View.java +@@ -11,7 +11,6 @@ import com.mojang.datafixers.types.Type; + import com.mojang.serialization.DynamicOps; + + import java.util.Objects; +-import java.util.Optional; + import java.util.function.Function; + + public final class View implements App2 { +@@ -73,9 +72,15 @@ public final class View implements App2 { + return Objects.hash(type, newType, function); + } + +- public Optional> rewrite(final PointFreeRule rule) { +- return rule.rewrite(DSL.func(type, newType), function()).map(f -> create(type, newType, f)); ++ // Yatopia start - Cadmium port ++ public View rewrite(final PointFreeRule rule) { ++ PointFree> result = rule.rewrite(DSL.func(type, newType), function()); ++ if (result!=null) { ++ return create(type, newType, result); ++ } ++ return null; + } ++ // Yatopia end + + public View rewriteOrNop(final PointFreeRule rule) { + return DataFixUtils.orElse(rewrite(rule), this); +diff --git a/src/main/java/com/mojang/datafixers/functions/Apply.java b/src/main/java/com/mojang/datafixers/functions/Apply.java +index 32c55622c7a6de4d4eb4c70c62f4fbf11385667b..89793b452e95c9ae8bec42d22bebd762533a8046 100644 +--- a/src/main/java/com/mojang/datafixers/functions/Apply.java ++++ b/src/main/java/com/mojang/datafixers/functions/Apply.java +@@ -3,11 +3,11 @@ + package com.mojang.datafixers.functions; + + import com.mojang.datafixers.DSL; ++import com.mojang.datafixers.DataFixUtils; // Yatopia - Cadmium port + import com.mojang.datafixers.types.Type; + import com.mojang.serialization.DynamicOps; + + import java.util.Objects; +-import java.util.Optional; + import java.util.function.Function; + + final class Apply extends PointFree { +@@ -31,20 +31,33 @@ final class Apply extends PointFree { + return "(ap " + func.toString(level + 1) + "\n" + indent(level + 1) + arg.toString(level + 1) + "\n" + indent(level) + ")"; + } + ++ // Yatopia start - Cadmium port + @Override +- public Optional> all(final PointFreeRule rule, final Type type) { +- return Optional.of(Functions.app( +- rule.rewrite(DSL.func(argType, type), func).map(f1 -> (PointFree>) f1).orElse(func), +- rule.rewrite(argType, arg).map(f -> (PointFree) f).orElse(arg), +- argType +- )); ++ public PointFree all(final PointFreeRule rule, final Type type) { ++ return Functions.app( ++ DataFixUtils.orElse(rule.rewrite(DSL.func(argType, type), func), func), ++ DataFixUtils.orElse(rule.rewrite(argType, arg), arg), ++ argType ++ ); + } + + @Override +- public Optional> one(final PointFreeRule rule, final Type type) { +- return rule.rewrite(DSL.func(argType, type), func).map(f -> Optional.of(Functions.app(f, arg, argType))) +- .orElseGet(() -> rule.rewrite(argType, arg).map(a -> Functions.app(func, a, argType))); ++ public PointFree one(final PointFreeRule rule, final Type type) { ++ final PointFree> result1 = rule.rewrite(DSL.func(argType, type), func); ++ ++ if (result1!=null) { ++ return Functions.app(result1, arg, argType); ++ } ++ ++ final PointFree result2 = rule.rewrite(argType, arg); ++ ++ if (result2!=null) { ++ return Functions.app(func, result2, argType); ++ } ++ ++ return null; + } ++ // Yatopia end + + @Override + public boolean equals(final Object o) { +diff --git a/src/main/java/com/mojang/datafixers/functions/Comp.java b/src/main/java/com/mojang/datafixers/functions/Comp.java +index a7a0ba66c715709801c2839018a6bb19df5cfdc6..6ea5ecad576f880783b9503ec370a5171d4f4da4 100644 +--- a/src/main/java/com/mojang/datafixers/functions/Comp.java ++++ b/src/main/java/com/mojang/datafixers/functions/Comp.java +@@ -3,12 +3,12 @@ + package com.mojang.datafixers.functions; + + import com.mojang.datafixers.DSL; ++import com.mojang.datafixers.DataFixUtils; // Yatopia - Cadmium port + import com.mojang.datafixers.types.Func; + import com.mojang.datafixers.types.Type; + import com.mojang.serialization.DynamicOps; + + import java.util.Objects; +-import java.util.Optional; + import java.util.function.Function; + + final class Comp extends PointFree> { +@@ -28,21 +28,34 @@ final class Comp extends PointFree> { + } + + @Override +- public Optional>> all(final PointFreeRule rule, final Type> type) { ++ // Yatopia start - Cadmium port ++ public PointFree> all(final PointFreeRule rule, final Type> type) { + final Func funcType = (Func) type; +- return Optional.of(Functions.comp( +- middleType, +- rule.rewrite(DSL.func(middleType, funcType.second()), first).map(f -> (PointFree>) f).orElse(first), +- rule.rewrite(DSL.func(funcType.first(), middleType), second).map(f1 -> (PointFree>) f1).orElse(second) +- )); ++ return Functions.comp( ++ middleType, ++ DataFixUtils.orElse(rule.rewrite(DSL.func(middleType, funcType.second()), first), first), ++ DataFixUtils.orElse(rule.rewrite(DSL.func(funcType.first(), middleType), second), second) ++ ); + } + + @Override +- public Optional>> one(final PointFreeRule rule, final Type> type) { ++ public PointFree> one(final PointFreeRule rule, final Type> type) { + final Func funcType = (Func) type; +- return rule.rewrite(DSL.func(middleType, funcType.second()), first).map(f -> Optional.of(Functions.comp(middleType, f, second))) +- .orElseGet(() -> rule.rewrite(DSL.func(funcType.first(), middleType), second).map(s -> Functions.comp(middleType, first, s))); ++ final PointFree> result1 = rule.rewrite(DSL.func(middleType, funcType.second()), first); ++ ++ if (result1!=null) { ++ return Functions.comp(middleType, result1, second); ++ } ++ ++ final PointFree> result2 = rule.rewrite(DSL.func(funcType.first(), middleType), second); ++ ++ if (result2!=null) { ++ return Functions.comp(middleType, first, result2); ++ } ++ ++ return null; + } ++ // Yatopia end + + @Override + public boolean equals(final Object o) { +diff --git a/src/main/java/com/mojang/datafixers/functions/PointFree.java b/src/main/java/com/mojang/datafixers/functions/PointFree.java +index f7593e3baf71a8992aa094719d9a369399996d46..b743d6d7188c53ac04ec087d9f89b6f87374e811 100644 +--- a/src/main/java/com/mojang/datafixers/functions/PointFree.java ++++ b/src/main/java/com/mojang/datafixers/functions/PointFree.java +@@ -7,7 +7,6 @@ import com.mojang.serialization.DynamicOps; + import org.apache.commons.lang3.StringUtils; + + import javax.annotation.Nullable; +-import java.util.Optional; + import java.util.function.Function; + + public abstract class PointFree { +@@ -30,13 +29,15 @@ public abstract class PointFree { + + public abstract Function, T> eval(); + +- Optional> all(final PointFreeRule rule, final Type type) { +- return Optional.of(this); ++ // Yatopia start - Cadmium port ++ PointFree all(final PointFreeRule rule, final Type type) { ++ return this; + } + +- Optional> one(final PointFreeRule rule, final Type type) { +- return Optional.empty(); ++ PointFree one(final PointFreeRule rule, final Type type) { ++ return null; + } ++ // Yatopia end + + @Override + public final String toString() { +diff --git a/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java b/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java +index eaee52d2eb5c9051b1114ae307022d83984e5840..0e31fe8412cef25b209c72a628756d5b67cce79f 100644 +--- a/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java ++++ b/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java +@@ -25,16 +25,23 @@ import org.apache.commons.lang3.ObjectUtils; + import java.util.BitSet; + import java.util.List; + import java.util.Objects; +-import java.util.Optional; + import java.util.function.Function; + import java.util.function.Supplier; + + public interface PointFreeRule { +- Optional> rewrite(final Type type, final PointFree expr); ++ // Yatopia start - Cadmium port ++ PointFree rewrite(final Type type, final PointFree expr); + +- default Optional> rewrite(final View view) { +- return rewrite(view.getFuncType(), view.function()).map(pf -> View.create(view.type(), view.newType(), pf)); ++ default View rewrite(final View view) { ++ PointFree> result = rewrite(view.getFuncType(), view.function()); ++ ++ if (result!=null) { ++ return View.create(view.type(), view.newType(), result); ++ } ++ ++ return null; + } ++ // Yatopia end + + default PointFree rewriteOrNop(final Type type, final PointFree expr) { + return DataFixUtils.orElse(rewrite(type, expr), expr); +@@ -52,9 +59,11 @@ public interface PointFreeRule { + INSTANCE; + + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { +- return Optional.of(expr); ++ // Yatopia start - Cadmium port ++ public PointFree rewrite(final Type type, final PointFree expr) { ++ return expr; + } ++ // Yatopia end + + @Override + public PointFreeRule get() { +@@ -67,25 +76,28 @@ public interface PointFreeRule { + + @SuppressWarnings("unchecked") + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ // Yatopia start - Cadmium port ++ public PointFree rewrite(final Type type, final PointFree expr) { + if (expr instanceof Bang) { +- return Optional.empty(); ++ return null; + } + if (type instanceof Func) { + final Func func = (Func) type; + if (func.second() instanceof EmptyPart) { +- return Optional.of((PointFree) Functions.bang()); ++ return (PointFree) Functions.bang(); + } + } +- return Optional.empty(); ++ return null; + } ++ // Yatopia end + } + + enum CompAssocLeft implements PointFreeRule { + INSTANCE; + + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ // Yatopia start - Cadmium port ++ public PointFree rewrite(final Type type, final PointFree expr) { + if (expr instanceof Comp) { + final Comp comp2 = (Comp) expr; + final PointFree> second = comp2.second; +@@ -94,13 +106,14 @@ public interface PointFreeRule { + return swap(comp1, comp2); + } + } +- return Optional.empty(); ++ return null; + } ++ // Yatopia end + + @SuppressWarnings("unchecked") +- private static Optional> swap(final Comp comp1, final Comp comp2raw) { ++ private static PointFree swap(final Comp comp1, final Comp comp2raw) { // Yatopia - Cadmium port + final Comp comp2 = (Comp) comp2raw; +- return Optional.of((PointFree) new Comp<>(comp1.middleType, new Comp<>(comp2.middleType, comp2.first, comp1.first), comp1.second)); ++ return (PointFree) new Comp<>(comp1.middleType, new Comp<>(comp2.middleType, comp2.first, comp1.first), comp1.second); // Yatopia - Cadmium port + } + } + +@@ -108,7 +121,7 @@ public interface PointFreeRule { + INSTANCE; + + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ public PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + if (expr instanceof Comp) { + final Comp comp1 = (Comp) expr; + final PointFree> first = comp1.first; +@@ -117,13 +130,13 @@ public interface PointFreeRule { + return swap(comp1, comp2); + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +- private static Optional> swap(final Comp comp1, final Comp comp2raw) { ++ private static PointFree swap(final Comp comp1, final Comp comp2raw) { // Yatopia - Cadmium port + final Comp comp2 = (Comp) comp2raw; +- return Optional.of((PointFree) new Comp<>(comp2.middleType, comp2.first, new Comp<>(comp1.middleType, comp2.second, comp1.second))); ++ return (PointFree) new Comp<>(comp2.middleType, comp2.first, new Comp<>(comp1.middleType, comp2.second, comp1.second)); // Yatopia - Cadmium port + } + } + +@@ -133,15 +146,15 @@ public interface PointFreeRule { + // (ap lens id) -> id + @SuppressWarnings("unchecked") + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ public PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + if (expr instanceof Apply) { + final Apply apply = (Apply) expr; + final PointFree> func = apply.func; + if (func instanceof ProfunctorTransformer && Objects.equals(apply.arg, Functions.id())) { +- return Optional.of((PointFree) Functions.id()); ++ return (PointFree) Functions.id(); // Yatopia - Cadmium port + } + } +- return Optional.empty(); ++ return null; + } + } + +@@ -150,7 +163,7 @@ public interface PointFreeRule { + + // (ap f1 (ap f2 arg)) -> (ap (f1 ◦ f2) arg) + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ public PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + if (expr instanceof Apply) { + final Apply applyFirst = (Apply) expr; + if (applyFirst.arg instanceof Apply) { +@@ -158,46 +171,54 @@ public interface PointFreeRule { + return cap(applyFirst, applySecond); + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +- private Optional> cap(final Apply applyFirst, final Apply applySecond) { ++ private PointFree cap(final Apply applyFirst, final Apply applySecond) { // Yatopia - Cadmium port + final PointFree func = applySecond.func; +- return Optional.of((PointFree) Functions.app(Functions.comp(applyFirst.argType, applyFirst.func, (PointFree>) func), applySecond.arg, applySecond.argType)); ++ return (PointFree) Functions.app(Functions.comp(applyFirst.argType, applyFirst.func, (PointFree>) func), applySecond.arg, applySecond.argType); // Yatopia - Cadmium port + } + } + + interface CompRewrite extends PointFreeRule { + @Override +- default Optional> rewrite(final Type type, final PointFree expr) { ++ default PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + if (expr instanceof Comp) { + final Comp comp = (Comp) expr; + final PointFree> first = comp.first; + final PointFree> second = comp.second; + if (first instanceof Comp) { + final Comp firstComp = (Comp) first; +- return doRewrite(type, comp.middleType, firstComp.second, comp.second).map(result -> { ++ final PointFree result = doRewrite(type, comp.middleType, firstComp.second, comp.second); // Yatopia - Cadmium port ++ ++ if (result!=null) { // Yatopia - Cadmium port + if (result instanceof Comp) { + final Comp resultComp = (Comp) result; + return buildLeftNested(resultComp, firstComp); + } + return buildRight(firstComp, result); +- }); ++ } // Yatopia - Cadmium port ++ ++ return null; + } + if (second instanceof Comp) { + final Comp secondComp = (Comp) second; +- return doRewrite(type, comp.middleType, comp.first, secondComp.first).map(result -> { ++ final PointFree result = doRewrite(type, comp.middleType, comp.first, secondComp.first); // Yatopia - Cadmium port ++ ++ if (result!=null) { // Yatopia - Cadmium port + if (result instanceof Comp) { + final Comp resultComp = (Comp) result; + return buildRightNested(secondComp, resultComp); + } + return buildLeft(result, secondComp); +- }); ++ } ++ ++ return null; // Yatopia - Cadmium port + } +- return (Optional>) doRewrite(type, comp.middleType, comp.first, comp.second); ++ return (PointFree) doRewrite(type, comp.middleType, comp.first, comp.second); // Yatopia - Cadmium port + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +@@ -222,7 +243,7 @@ public interface PointFreeRule { + return (PointFree) new Comp<>(comp2.middleType, comp2.first, new Comp<>(comp1.middleType, comp2.second, comp1.second)); + } + +- Optional> doRewrite(Type type, Type middleType, PointFree> first, PointFree> second); ++ PointFree doRewrite(Type type, Type middleType, PointFree> first, PointFree> second); // Yatopia - Cadmium port + } + + enum SortProj implements CompRewrite { +@@ -230,7 +251,7 @@ public interface PointFreeRule { + + // (ap π1 f)◦(ap π2 g) -> (ap π2 g)◦(ap π1 f) + @Override +- public Optional> doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { ++ public PointFree doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { // Yatopia - Cadmium port + if (first instanceof Apply && second instanceof Apply) { + final Apply applyFirst = (Apply) first; + final Apply applySecond = (Apply) second; +@@ -253,11 +274,11 @@ public interface PointFreeRule { + if (Objects.equals(fo, Optics.proj2()) && Objects.equals(so, Optics.proj1())) { + final Func firstArg = (Func) applyFirst.argType; + final Func secondArg = (Func) applySecond.argType; +- return Optional.of(cap(firstArg, secondArg, applyFirst, applySecond)); ++ return cap(firstArg, secondArg, applyFirst, applySecond); // Yatopia - Cadmium port + } + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +@@ -275,7 +296,7 @@ public interface PointFreeRule { + + // (ap i1 f)◦(ap i2 g) -> (ap i2 g)◦(ap i1 f) + @Override +- public Optional> doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { ++ public PointFree doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { // Yatopia - Cadmium port + if (first instanceof Apply && second instanceof Apply) { + final Apply applyFirst = (Apply) first; + final Apply applySecond = (Apply) second; +@@ -298,11 +319,11 @@ public interface PointFreeRule { + if (Objects.equals(fo, Optics.inj2()) && Objects.equals(so, Optics.inj1())) { + final Func firstArg = (Func) applyFirst.argType; + final Func secondArg = (Func) applySecond.argType; +- return Optional.of(cap(firstArg, secondArg, applyFirst, applySecond)); ++ return cap(firstArg, secondArg, applyFirst, applySecond); // Yatopia - Cadmium port + } + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +@@ -320,7 +341,7 @@ public interface PointFreeRule { + + // Optic[o1] ◦ Optic[o2] -> Optic[o1 ◦ o2] + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ public PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + if (expr instanceof Comp) { + final Comp comp = (Comp) expr; + final PointFree> first = comp.first; +@@ -328,10 +349,10 @@ public interface PointFreeRule { + if (first instanceof ProfunctorTransformer && second instanceof ProfunctorTransformer) { + final ProfunctorTransformer firstOptic = (ProfunctorTransformer) first; + final ProfunctorTransformer secondOptic = (ProfunctorTransformer) second; +- return Optional.of(cap(firstOptic, secondOptic)); ++ return cap(firstOptic, secondOptic); // Yatopia - Cadmium port + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +@@ -345,9 +366,8 @@ public interface PointFreeRule { + INSTANCE; + + // (ap lens f)◦(ap lens g) -> (ap lens (f ◦ g)) +- @SuppressWarnings("unchecked") + @Override +- public Optional> doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { ++ public PointFree doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { // Yatopia - Cadmium port + if (first instanceof Apply && second instanceof Apply) { + final Apply applyFirst = (Apply) first; + final Apply applySecond = (Apply) second; +@@ -364,17 +384,17 @@ public interface PointFreeRule { + } + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + +- private Optional> cap(final ProfunctorTransformer l1, final ProfunctorTransformer l2, final PointFree f1, final PointFree f2, final Func firstType, final Func secondType) { ++ private PointFree cap(final ProfunctorTransformer l1, final ProfunctorTransformer l2, final PointFree f1, final PointFree f2, final Func firstType, final Func secondType) { // Yatopia - Cadmium port + return cap2(l1, (ProfunctorTransformer) l2, (PointFree>) f1, (PointFree>) f2, (Func) firstType, (Func) secondType); + } + +- private Optional> cap2(final ProfunctorTransformer l1, final ProfunctorTransformer l2, final PointFree> f1, final PointFree> f2, final Func firstType, final Func secondType) { ++ private PointFree cap2(final ProfunctorTransformer l1, final ProfunctorTransformer l2, final PointFree> f1, final PointFree> f2, final Func firstType, final Func secondType) { // Yatopia - Cadmium port + final PointFree, Function>> lens = (PointFree, Function>>) (PointFree) l1; + final PointFree> arg = Functions.comp(firstType.first(), f1, f2); +- return Optional.of((PointFree) Functions.app(lens, arg, DSL.func(secondType.first(), firstType.second()))); ++ return (PointFree) Functions.app(lens, arg, DSL.func(secondType.first(), firstType.second())); // Yatopia - Cadmium port + } + } + +@@ -382,15 +402,14 @@ public interface PointFreeRule { + INSTANCE; + + // (fold g ◦ in) ◦ fold (f ◦ in) -> fold ( g ◦ f ◦ in), <== g ◦ in ◦ fold (f ◦ in) ◦ out == in ◦ fold (f ◦ in) ◦ out ◦ g <== g doesn't touch fold's index +- @SuppressWarnings("unchecked") + @Override +- public Optional> doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { ++ public PointFree doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { // Yatopia - Cadmium port + if (first instanceof Fold && second instanceof Fold) { + // fold (_) ◦ fold (_) + final Fold firstFold = (Fold) first; + final Fold secondFold = (Fold) second; + final RecursiveTypeFamily family = firstFold.aType.family(); +- if (Objects.equals(family, secondFold.aType.family()) && firstFold.index == secondFold.index) { ++ if (firstFold.index==secondFold.index && Objects.equals(family, secondFold.aType.family())) { // Yatopia - Cadmium port + // same fold + final List> newAlgebra = Lists.newArrayList(); + +@@ -399,8 +418,8 @@ public interface PointFreeRule { + boolean foundOne = false; + for (int i = 0; i < family.size(); i++) { + final RewriteResult firstAlgFunc = firstFold.algebra.apply(i); +- final RewriteResult secondAlgFunc = secondFold.algebra.apply(i); + final boolean firstId = Objects.equals(CompAssocRight.INSTANCE.rewriteOrNop(firstAlgFunc.view()).function(), Functions.id()); ++ /* + final boolean secondId = Objects.equals(secondAlgFunc.view().function(), Functions.id()); + + if (firstId && secondId) { +@@ -408,15 +427,37 @@ public interface PointFreeRule { + } else if (!foundOne && !firstId && !secondId) { + newAlgebra.add(getCompose(firstAlgFunc, secondAlgFunc)); + foundOne = true; ++ */ ++ // Yatopia start - Cadmium port ++ if (firstId) { ++ final RewriteResult secondAlgFunc = secondFold.algebra.apply(i); ++ final boolean secondId = Objects.equals(secondAlgFunc.view().function(), Functions.id()); ++ ++ if (secondId) { ++ newAlgebra.add(firstFold.algebra.apply(i)); ++ } else { ++ return null; ++ } ++ } else if (!foundOne) { ++ final RewriteResult secondAlgFunc = secondFold.algebra.apply(i); ++ final boolean secondId = Objects.equals(secondAlgFunc.view().function(), Functions.id()); ++ ++ if (!secondId) { ++ newAlgebra.add(getCompose(firstAlgFunc, secondAlgFunc)); ++ foundOne = true; ++ } else { ++ return null; ++ } ++ // Yatopia end + } else { +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + } + final Algebra algebra = new ListAlgebra("FusedSame", newAlgebra); +- return Optional.of((PointFree) family.fold(algebra).apply(firstFold.index).view().function()); ++ return family.fold(algebra).apply(firstFold.index).view().function(); // Yatopia - Cadmium port + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + + @SuppressWarnings("unchecked") +@@ -431,13 +472,14 @@ public interface PointFreeRule { + // (fold g ◦ in) ◦ fold (f ◦ in) -> fold ( g ◦ f ◦ in), <== g ◦ in ◦ fold (f ◦ in) ◦ out == in ◦ fold (f ◦ in) ◦ out ◦ g <== g doesn't touch fold's index + @SuppressWarnings("unchecked") + @Override +- public Optional> doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { ++ public PointFree doRewrite(final Type type, final Type middleType, final PointFree> first, final PointFree> second) { // Yatopia - Cadmium port + if (first instanceof Fold && second instanceof Fold) { + // fold (_) ◦ fold (_) + final Fold firstFold = (Fold) first; + final Fold secondFold = (Fold) second; + final RecursiveTypeFamily family = firstFold.aType.family(); +- if (Objects.equals(family, secondFold.aType.family()) && firstFold.index == secondFold.index) { ++ // if (Objects.equals(family, secondFold.aType.family()) && firstFold.index == secondFold.index) { ++ if (firstFold.index==secondFold.index && Objects.equals(family, secondFold.aType.family())) { // Yatopia - Cadmium port + // same fold + final List> newAlgebra = Lists.newArrayList(); + +@@ -460,46 +502,57 @@ public interface PointFreeRule { + // TODO: verify that this is enough + for (int i = 0; i < family.size(); i++) { + final RewriteResult firstAlgFunc = firstFold.algebra.apply(i); ++ // Yatopia start - Cadmium port ++ if (firstAlgFunc.recData().intersects(secondModifies)) { ++ // outer function depends on the result of the inner one ++ return null; ++ } ++ // Yatopia end + final RewriteResult secondAlgFunc = secondFold.algebra.apply(i); ++ /* + final PointFree firstF = CompAssocRight.INSTANCE.rewriteOrNop(firstAlgFunc.view()).function(); + final PointFree secondF = CompAssocRight.INSTANCE.rewriteOrNop(secondAlgFunc.view()).function(); + final boolean firstId = Objects.equals(firstF, Functions.id()); + final boolean secondId = Objects.equals(secondF, Functions.id()); + if (firstAlgFunc.recData().intersects(secondModifies) || secondAlgFunc.recData().intersects(firstModifies)) { ++ */ ++ // Yatopia start - Cadmium port ++ if (secondAlgFunc.recData().intersects(firstModifies)) { + // outer function depends on the result of the inner one +- return Optional.empty(); ++ return null; + } +- if (firstId) { ++ // Yatopia end ++ if (Objects.equals(CompAssocRight.INSTANCE.rewriteOrNop(firstAlgFunc.view()).function(), Functions.id())) { // Yatopia - Cadmium port + newAlgebra.add(secondAlgFunc); +- } else if (secondId) { ++ } else if (Objects.equals(CompAssocRight.INSTANCE.rewriteOrNop(secondAlgFunc.view()).function(), Functions.id())) { // Yatopia - Cadmium port + newAlgebra.add(firstAlgFunc); + } else { +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + } + // have new algebra - make a new fold + + final Algebra algebra = new ListAlgebra("FusedDifferent", newAlgebra); +- return Optional.of((PointFree) family.fold(algebra).apply(firstFold.index).view().function()); ++ return family.fold(algebra).apply(firstFold.index).view().function(); // Yatopia - Cadmium port + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + } + + /*final class ReflexCata implements Rule { + @SuppressWarnings("unchecked") + @Override +- public Optional> rewrite(final Type type, final PF expr) { ++ public PF rewrite(final Type type, final PF expr) { // Yatopia - Cadmium port + if (type instanceof Type.Func && expr instanceof PF.Cata) { + final Type.Func funcType = (Type.Func) type; + final PF.Cata cata = (PF.Cata) expr; + // TODO better equality + if (Objects.equals(cata.alg, PF.genBF(cata.family.to)) && Objects.equals(funcType.first, funcType.second)) { +- return Optional.of((PF) PF.id()); ++ return (PF) PF.id(); // Yatopia - Cadmium port + } + } +- return Optional.empty(); ++ return null; // Yatopia - Cadmium port + } + }*/ + +@@ -518,14 +571,20 @@ public interface PointFreeRule { + this.rules = ImmutableList.copyOf(rules); + } + ++ // Yatopia start - Cadmium port + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { +- Optional> result = Optional.of(expr); ++ public PointFree rewrite(final Type type, final PointFree expr) { ++ PointFree result = expr; + for (final Supplier rule : rules) { +- result = result.flatMap(pf -> rule.get().rewrite(type, pf)); ++ if (result==null) { ++ return null; ++ } ++ ++ result = rule.get().rewrite(type, result); + } + return result; + } ++ // Yatopia end + + @Override + public boolean equals(final Object obj) { +@@ -546,30 +605,28 @@ public interface PointFreeRule { + } + + static PointFreeRule orElse(final PointFreeRule first, final PointFreeRule second) { +- return new OrElse(first, () -> second); +- } +- +- static PointFreeRule orElseStrict(final PointFreeRule first, final Supplier second) { + return new OrElse(first, second); + } + + final class OrElse implements PointFreeRule { + protected final PointFreeRule first; +- protected final Supplier second; ++ protected final PointFreeRule second; // Yatopia - Cadmium port + +- public OrElse(final PointFreeRule first, final Supplier second) { ++ public OrElse(final PointFreeRule first, final PointFreeRule second) { // Yatopia - Cadmium port + this.first = first; + this.second = second; + } + ++ // Yatopia start - Cadmium port + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { +- final Optional> view = first.rewrite(type, expr); +- if (view.isPresent()) { ++ public PointFree rewrite(final Type type, final PointFree expr) { ++ final PointFree view = first.rewrite(type, expr); ++ if (view!=null) { + return view; + } +- return second.get().rewrite(type, expr); ++ return second.rewrite(type, expr); + } ++ // Yatopia end + + @Override + public boolean equals(final Object obj) { +@@ -589,6 +646,42 @@ public interface PointFreeRule { + } + } + ++ // Yatopia start - Cadmium port ++ final class OrElseStrict implements PointFreeRule { ++ protected final PointFreeRule rule; ++ ++ public OrElseStrict(final PointFreeRule rule) { ++ this.rule = rule; ++ } ++ ++ @Override ++ public PointFree rewrite(final Type type, final PointFree expr) { ++ final PointFree view = rule.rewrite(type, expr); ++ if (view!=null) { ++ return view; ++ } ++ return expr.one(this, type); ++ } ++ ++ @Override ++ public boolean equals(final Object obj) { ++ if (obj == this) { ++ return true; ++ } ++ if (!(obj instanceof OrElse)) { ++ return false; ++ } ++ final OrElse that = (OrElse) obj; ++ return Objects.equals(rule, that.first); ++ } ++ ++ @Override ++ public int hashCode() { ++ return Objects.hash(rule); ++ } ++ } ++ // Yatopia end ++ + static PointFreeRule all(final PointFreeRule rule) { + return new All(rule); + } +@@ -598,7 +691,7 @@ public interface PointFreeRule { + } + + static PointFreeRule once(final PointFreeRule rule) { +- return orElseStrict(rule, () -> one(once(rule))); ++ return new OrElseStrict(rule); // Yatopia - Cadmium port + } + + static PointFreeRule many(final PointFreeRule rule) { +@@ -617,7 +710,7 @@ public interface PointFreeRule { + } + + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ public PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + return expr.all(rule, type); + } + +@@ -647,7 +740,7 @@ public interface PointFreeRule { + } + + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { ++ public PointFree rewrite(final Type type, final PointFree expr) { // Yatopia - Cadmium port + return expr.one(rule, type); + } + +@@ -676,17 +769,22 @@ public interface PointFreeRule { + this.rule = rule; + } + ++ // Yatopia start - Cadmium port + @Override +- public Optional> rewrite(final Type type, final PointFree expr) { +- Optional> result = Optional.of(expr); ++ public PointFree rewrite(final Type type, final PointFree expr) { ++ PointFree result = expr; + while (true) { +- final Optional> newResult = result.flatMap(e -> rule.rewrite(type, e).map(r -> r)); +- if (!newResult.isPresent()) { ++ if (result==null) { ++ return null; ++ } ++ final PointFree newResult = rule.rewrite(type, result); ++ if (newResult==null) { + return result; + } + result = newResult; + } + } ++ // Yatopia end + + @Override + public boolean equals(final Object o) { +diff --git a/src/main/java/com/mojang/datafixers/optics/IdAdapter.java b/src/main/java/com/mojang/datafixers/optics/IdAdapter.java +index 3f1793aca685a627d0e925d037f663a257522d8d..0e4bf4b58710be135e6573ccff3f8c0a2a41191a 100644 +--- a/src/main/java/com/mojang/datafixers/optics/IdAdapter.java ++++ b/src/main/java/com/mojang/datafixers/optics/IdAdapter.java +@@ -3,6 +3,14 @@ + package com.mojang.datafixers.optics; + + class IdAdapter implements Adapter { ++ // Yatopia start - Cadmium port ++ private static final Adapter INSTANCE = new IdAdapter<>(); ++ ++ private IdAdapter() { ++ ++ } ++ // Yatopia end ++ + @Override + public S from(final S s) { + return s; +@@ -22,4 +30,11 @@ class IdAdapter implements Adapter { + public String toString() { + return "id"; + } ++ ++ // Yatopia start - Cadmium port ++ @SuppressWarnings("unchecked") ++ public static Adapter instance() { ++ return (Adapter) INSTANCE; ++ } ++ // Yatopia end + } +diff --git a/src/main/java/com/mojang/datafixers/optics/Inj1.java b/src/main/java/com/mojang/datafixers/optics/Inj1.java +index 8c56955c9d6c3d6021d5c9fda452e39774ac587c..4813f335cd536d1b32b9095dbd76e18c22a6dea1 100644 +--- a/src/main/java/com/mojang/datafixers/optics/Inj1.java ++++ b/src/main/java/com/mojang/datafixers/optics/Inj1.java +@@ -5,6 +5,14 @@ package com.mojang.datafixers.optics; + import com.mojang.datafixers.util.Either; + + public final class Inj1 implements Prism, Either, F, F2> { ++ // Yatopia start - Cadmium port ++ private static final Inj1 INSTANCE = new Inj1<>(); ++ ++ private Inj1() { ++ ++ } ++ // Yatopia end ++ + @Override + public Either, F> match(final Either either) { + return either.map(Either::right, g -> Either.left(Either.right(g))); +@@ -24,4 +32,11 @@ public final class Inj1 implements Prism, Either, + public boolean equals(final Object obj) { + return obj instanceof Inj1; + } ++ ++ // Yatopia start - Cadmium port ++ @SuppressWarnings("unchecked") ++ public static Inj1 instance() { ++ return (Inj1) INSTANCE; ++ } ++ // Yatopia end + } +diff --git a/src/main/java/com/mojang/datafixers/optics/Inj2.java b/src/main/java/com/mojang/datafixers/optics/Inj2.java +index 2f95715f310faec6e43c715cbf204a092bfc107e..ad2400ccdb159cbf8b882d0b5afc53d3e220f770 100644 +--- a/src/main/java/com/mojang/datafixers/optics/Inj2.java ++++ b/src/main/java/com/mojang/datafixers/optics/Inj2.java +@@ -2,9 +2,18 @@ + // Licensed under the MIT license. + package com.mojang.datafixers.optics; + ++import com.mojang.datafixers.DataFixerBuilder; // Yatopia - Cadmium port + import com.mojang.datafixers.util.Either; + + public final class Inj2 implements Prism, Either, G, G2> { ++ // Yatopia start - Cadmium port ++ private static final Inj2 INSTANCE = new Inj2<>(); ++ ++ private Inj2() { ++ ++ } ++ // Yatopia end ++ + @Override + public Either, G> match(final Either either) { + return either.map(f -> Either.left(Either.left(f)), Either::right); +@@ -24,4 +33,11 @@ public final class Inj2 implements Prism, Either, + public boolean equals(final Object obj) { + return obj instanceof Inj2; + } ++ ++ // Yatopia start - Cadmium port ++ @SuppressWarnings("unchecked") ++ public static Inj2 instance() { ++ return (Inj2) INSTANCE; ++ } ++ // Yatopia end + } +diff --git a/src/main/java/com/mojang/datafixers/optics/Optics.java b/src/main/java/com/mojang/datafixers/optics/Optics.java +index 4bf469df6a9092a554df4dafdbfe37f51da28f32..cec492b8e633adedbf6c4d83cf257f640412de06 100644 +--- a/src/main/java/com/mojang/datafixers/optics/Optics.java ++++ b/src/main/java/com/mojang/datafixers/optics/Optics.java +@@ -65,7 +65,7 @@ public abstract class Optics { + } + + public static Adapter id() { +- return new IdAdapter<>(); ++ return IdAdapter.instance(); // Yatopia - Cadmium port + } + + public static Adapter adapter(final Function from, final Function to) { +@@ -223,19 +223,19 @@ public abstract class Optics { + } + + public static Proj1 proj1() { +- return new Proj1<>(); ++ return Proj1.instance(); // Yatopia - Cadmium port + } + + public static Proj2 proj2() { +- return new Proj2<>(); ++ return Proj2.instance(); // Yatopia - Cadmium port + } + + public static Inj1 inj1() { +- return new Inj1<>(); ++ return Inj1.instance(); // Yatopia - Cadmium port + } + + public static Inj2 inj2() { +- return new Inj2<>(); ++ return Inj2.instance(); // Yatopia - Cadmium port + } + + /*public static Optic, Either, A, B> choosing(final Optic first, final Optic second) { +diff --git a/src/main/java/com/mojang/datafixers/optics/Proj1.java b/src/main/java/com/mojang/datafixers/optics/Proj1.java +index 488e4cd9a87178a441eda0ee5bff9c5feca69e6d..ae8850e52feeff51f61c4527c792446ef5e23344 100644 +--- a/src/main/java/com/mojang/datafixers/optics/Proj1.java ++++ b/src/main/java/com/mojang/datafixers/optics/Proj1.java +@@ -5,6 +5,14 @@ package com.mojang.datafixers.optics; + import com.mojang.datafixers.util.Pair; + + public final class Proj1 implements Lens, Pair, F, F2> { ++ // Yatopia start - Cadmium port ++ private static final Proj1 INSTANCE = new Proj1<>(); ++ ++ private Proj1() { ++ ++ } ++ // Yatopia end ++ + @Override + public F view(final Pair pair) { + return pair.getFirst(); +@@ -24,4 +32,11 @@ public final class Proj1 implements Lens, Pair, F, F + public boolean equals(final Object obj) { + return obj instanceof Proj1; + } ++ ++ // Yatopia start - Cadmium port ++ @SuppressWarnings("unchecked") ++ public static Proj1 instance() { ++ return (Proj1) INSTANCE; ++ } ++ // Yatopia end + } +diff --git a/src/main/java/com/mojang/datafixers/optics/Proj2.java b/src/main/java/com/mojang/datafixers/optics/Proj2.java +index 3bd3689115f25d34fceaa8f8f915c21656267af5..76d05449689808e3d0435aea7480dcad134730f1 100644 +--- a/src/main/java/com/mojang/datafixers/optics/Proj2.java ++++ b/src/main/java/com/mojang/datafixers/optics/Proj2.java +@@ -5,6 +5,14 @@ package com.mojang.datafixers.optics; + import com.mojang.datafixers.util.Pair; + + public final class Proj2 implements Lens, Pair, G, G2> { ++ // Yatopia start - Cadmium port ++ private static final Proj2 INSTANCE = new Proj2<>(); ++ ++ private Proj2() { ++ ++ } ++ // Yatopia end ++ + @Override + public G view(final Pair pair) { + return pair.getSecond(); +@@ -24,4 +32,11 @@ public final class Proj2 implements Lens, Pair, G, G + public boolean equals(final Object obj) { + return obj instanceof Proj2; + } ++ ++ // Yatopia start - Cadmium port ++ @SuppressWarnings("unchecked") ++ public static Proj2 instance() { ++ return (Proj2) INSTANCE; ++ } ++ // Yatopia end + } +diff --git a/src/main/java/com/mojang/datafixers/types/Type.java b/src/main/java/com/mojang/datafixers/types/Type.java +index 4592da3c75e639164d10cc05518c27d8f488a103..53ab2332aaff5970e5f3825734906fc7f1f8c53c 100644 +--- a/src/main/java/com/mojang/datafixers/types/Type.java ++++ b/src/main/java/com/mojang/datafixers/types/Type.java +@@ -87,7 +87,7 @@ public abstract class Type implements App { + } + + public Optional> everywhere(final TypeRewriteRule rule, final PointFreeRule optimizationRule, final boolean recurse, final boolean checkIndex) { +- final TypeRewriteRule rule2 = TypeRewriteRule.seq(TypeRewriteRule.orElse(rule, TypeRewriteRule::nop), TypeRewriteRule.all(TypeRewriteRule.everywhere(rule, optimizationRule, recurse, checkIndex), recurse, checkIndex)); ++ final TypeRewriteRule rule2 = TypeRewriteRule.seq(TypeRewriteRule.orElse(rule, TypeRewriteRule.Nop.INSTANCE), TypeRewriteRule.all(TypeRewriteRule.everywhere(rule, optimizationRule, recurse, checkIndex), recurse, checkIndex)); // Yatopia - Cadmium port + return rewrite(rule2, optimizationRule); + } + +@@ -187,7 +187,17 @@ public abstract class Type implements App { + }); + + if (ref.getValue() != null) { +- Optional> result = rule.rewrite(this).flatMap(r -> r.view().rewrite(fRule).map(view -> RewriteResult.create(view, r.recData()))); ++ // Yatopia start - Cadmium port ++ Optional> result = rule.rewrite(this).flatMap(r -> { ++ View view = r.view().rewrite(fRule); ++ ++ if (view!=null) { ++ return Optional.of(RewriteResult.create(view, r.recData())); ++ } ++ ++ return Optional.empty(); ++ }); ++ // Yatopia end + REWRITE_CACHE.put(key, result); + pending.complete(result); + PENDING_REWRITE_CACHE.remove(key); diff --git a/patches/server/0061-Port-LazyDFU.patch b/patches/server/0061-Port-LazyDFU.patch new file mode 100644 index 00000000..5c388962 --- /dev/null +++ b/patches/server/0061-Port-LazyDFU.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Steinborn +Date: Tue, 2 Feb 2021 20:59:35 -0500 +Subject: [PATCH] Port LazyDFU + + +diff --git a/src/main/java/me/steinborn/lazydfu/mod/LazyDataFixerBuilder.java b/src/main/java/me/steinborn/lazydfu/mod/LazyDataFixerBuilder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..081aa46531a9df1dc732669a2c1e3180790468c4 +--- /dev/null ++++ b/src/main/java/me/steinborn/lazydfu/mod/LazyDataFixerBuilder.java +@@ -0,0 +1,23 @@ ++package me.steinborn.lazydfu.mod; ++ ++import com.mojang.datafixers.DataFixerBuilder; ++import com.mojang.datafixers.DataFixer; ++ ++import java.util.concurrent.Executor; ++ ++/** ++ * This version of {@code DataFixerBuilder} does not immediately initialize rules. ++ */ ++public class LazyDataFixerBuilder extends DataFixerBuilder { ++ ++ private static final Executor NO_OP_EXECUTOR = command -> {}; ++ ++ public LazyDataFixerBuilder(int dataVersion) { ++ super(dataVersion); ++ } ++ ++ @Override ++ public DataFixer build(Executor executor) { ++ return super.build(NO_OP_EXECUTOR); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/minecraft/util/datafix/DataConverterRegistry.java b/src/main/java/net/minecraft/util/datafix/DataConverterRegistry.java +index 625dcfaeffb83a0b8b53778c32287cac8daae540..5a22fd08cc2a09d8695ca060dee80183364164a1 100644 +--- a/src/main/java/net/minecraft/util/datafix/DataConverterRegistry.java ++++ b/src/main/java/net/minecraft/util/datafix/DataConverterRegistry.java +@@ -202,7 +202,7 @@ public class DataConverterRegistry { + // Yatopia start + private static final DataFixer c; + static { +- DataFixerBuilder datafixerbuilder = new DataFixerBuilder(SharedConstants.getGameVersion().getWorldVersion()); ++ DataFixerBuilder datafixerbuilder = new me.steinborn.lazydfu.mod.LazyDataFixerBuilder(SharedConstants.getGameVersion().getWorldVersion()); // Yatopia - Port LazyDFU + + a(datafixerbuilder); + c = datafixerbuilder.build(SystemUtils.d); diff --git a/patches/server/0062-Lithium-CompactSineLUT.patch b/patches/server/0062-Lithium-CompactSineLUT.patch new file mode 100644 index 00000000..52daea0b --- /dev/null +++ b/patches/server/0062-Lithium-CompactSineLUT.patch @@ -0,0 +1,138 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JellySquid +Date: Fri, 5 Feb 2021 00:16:30 -0600 +Subject: [PATCH] Lithium: CompactSineLUT + +Original code by JellySquid, licensed under GNU Lesser General Public License v3.0 +you can find the original code on https://github.com/CaffeineMC/lithium-fabric/ (Yarn mappings) + +diff --git a/src/main/java/net/minecraft/util/MathHelper.java b/src/main/java/net/minecraft/util/MathHelper.java +index e9e7fcf2b63febe2a7d055826fabb86bc13a5cf3..b95115aca72ba0cf6451096ddbd8b50a8f3bb5c6 100644 +--- a/src/main/java/net/minecraft/util/MathHelper.java ++++ b/src/main/java/net/minecraft/util/MathHelper.java +@@ -5,6 +5,7 @@ import java.util.UUID; + import java.util.function.IntPredicate; + import net.minecraft.SystemUtils; + import net.minecraft.core.BaseBlockPosition; ++import org.yatopiamc.yatopia.server.math.CompactSineLUT; + + public class MathHelper { + +@@ -15,6 +16,7 @@ public class MathHelper { + } + + }); ++ public static float[] getSinTable() { return b; } // Yatopia - OBFHELPER + private static final Random c = new Random(); + private static final int[] d = new int[]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; + private static final double e = Double.longBitsToDouble(4805340802404319232L); +@@ -22,11 +24,11 @@ public class MathHelper { + private static final double[] g = new double[257]; + + public static float sin(float f) { +- return MathHelper.b[(int) (f * 10430.378F) & '\uffff']; ++ return CompactSineLUT.sin(f); // Yatopia - CompactSineLUT + } + + public static float cos(float f) { +- return MathHelper.b[(int) (f * 10430.378F + 16384.0F) & '\uffff']; ++ return CompactSineLUT.cos(f); // Yatopia - CompactSineLUT + } + + public static float c(float f) { +diff --git a/src/main/java/org/yatopiamc/yatopia/server/math/CompactSineLUT.java b/src/main/java/org/yatopiamc/yatopia/server/math/CompactSineLUT.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1bb8ba3836af872bc2e58898f16a0b91036b9b02 +--- /dev/null ++++ b/src/main/java/org/yatopiamc/yatopia/server/math/CompactSineLUT.java +@@ -0,0 +1,90 @@ ++package org.yatopiamc.yatopia.server.math; ++ ++import net.minecraft.util.MathHelper; ++ ++/** ++ * A replacement for the sine angle lookup table used in {@link MathHelper}, both reducing the size of LUT and improving ++ * the access patterns for common paired sin/cos operations. ++ * ++ * sin(-x) = -sin(x) ++ * ... to eliminate negative angles from the LUT. ++ * ++ * sin(x) = sin(pi/2 - x) ++ * ... to eliminate supplementary angles from the LUT. ++ * ++ * Using these identities allows us to reduce the LUT from 64K entries (256 KB) to just 16K entries (64 KB), enabling ++ * it to better fit into the CPU's caches at the expense of some cycles on the fast path. The implementation has been ++ * tightly optimized to avoid branching where possible and to use very quick integer operations. ++ * ++ * Generally speaking, reducing the size of a lookup table is always a good optimization, but since we need to spend ++ * extra CPU cycles trying to maintain parity with vanilla, there is the potential risk that this implementation ends ++ * up being slower than vanilla when the lookup table is able to be kept in cache memory. ++ * ++ * Unlike other "fast math" implementations, the values returned by this class are *bit-for-bit identical* with those ++ * from {@link MathHelper}. Validation is performed during runtime to ensure that the table is correct. ++ * ++ * @author coderbot16 Author of the original (and very clever) implementation in Rust: ++ * https://gitlab.com/coderbot16/i73/-/tree/master/i73-trig/src ++ * @author jellysquid3 Additional optimizations, port to Java ++ */ ++public class CompactSineLUT { ++ private static final int[] SINE_TABLE_INT = new int[16384 + 1]; ++ private static final float SINE_TABLE_MIDPOINT; ++ ++ static { ++ final float[] SINE_TABLE = MathHelper.getSinTable(); ++ // Copy the sine table, covering to raw int bits ++ for (int i = 0; i < SINE_TABLE_INT.length; i++) { ++ SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]); ++ } ++ ++ SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2]; ++ ++ // Test that the lookup table is correct during runtime ++ for (int i = 0; i < SINE_TABLE.length; i++) { ++ float expected = SINE_TABLE[i]; ++ float value = lookup(i); ++ ++ if (expected != value) { ++ throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value)); ++ } ++ } ++ } ++ ++ // [VanillaCopy] MathHelper#sin(float) ++ public static float sin(float f) { ++ return lookup((int) (f * 10430.38) & 0xFFFF); ++ } ++ ++ // [VanillaCopy] MathHelper#cos(float) ++ public static float cos(float f) { ++ return lookup((int) (f * 10430.38 + 16384.0) & 0xFFFF); ++ } ++ ++ private static float lookup(int index) { ++ // A special case... Is there some way to eliminate this? ++ if (index == 32768) { ++ return SINE_TABLE_MIDPOINT; ++ } ++ ++ // Trigonometric identity: sin(-x) = -sin(x) ++ // Given a domain of 0 <= x <= 2*pi, just negate the value if x > pi. ++ // This allows the sin table size to be halved. ++ int neg = (index & 0x8000) << 16; ++ ++ // All bits set if (pi/2 <= x), none set otherwise ++ // Extracts the 15th bit from 'half' ++ int mask = (index << 17) >> 31; ++ ++ // Trigonometric identity: sin(x) = sin(pi/2 - x) ++ int pos = (0x8001 & mask) + (index ^ mask); ++ ++ // Wrap the position in the table. Moving this down to immediately before the array access ++ // seems to help the Hotspot compiler optimize the bit math better. ++ pos &= 0x7fff; ++ ++ // Fetch the corresponding value from the LUT and invert the sign bit as needed ++ // This directly manipulate the sign bit on the float bits to simplify logic ++ return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg); ++ } ++} diff --git a/patches/server/0063-tic-tacs-unblocking.patch b/patches/server/0063-tic-tacs-unblocking.patch new file mode 100644 index 00000000..89988522 --- /dev/null +++ b/patches/server/0063-tic-tacs-unblocking.patch @@ -0,0 +1,407 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Gegy +Date: Tue, 9 Feb 2021 13:23:12 -0500 +Subject: [PATCH] tic-tacs: unblocking + +Code originally licenced under LGPLv3 for the tic-tacs project: https://github.com/Gegy/tic-tacs + +diff --git a/src/main/java/net/gegy1000/tictacs/NonBlockingWorldAccess.java b/src/main/java/net/gegy1000/tictacs/NonBlockingWorldAccess.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0d99f3b4930045632d88fa4acb3b8159ee9cf9f3 +--- /dev/null ++++ b/src/main/java/net/gegy1000/tictacs/NonBlockingWorldAccess.java +@@ -0,0 +1,29 @@ ++package net.gegy1000.tictacs; ++ ++import net.minecraft.world.level.block.state.IBlockData; ++import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.material.Fluid; ++import net.minecraft.world.level.material.FluidTypes; ++import net.minecraft.core.BlockPosition; ++import net.minecraft.world.level.IWorldReader; ++import net.minecraft.world.level.material.FluidTypes; ++ ++public interface NonBlockingWorldAccess extends IWorldReader { ++ ++ public static final IBlockData DEFAULT_BLOCK_STATE = Blocks.AIR.getBlockData(); ++ public static final Fluid DEFAULT_FLUID_STATE = FluidTypes.EMPTY.getFluidData(); ++ ++ default IBlockData getBlockStateIfLoaded(BlockPosition pos) { ++ if (this.isLoaded(pos)) { ++ return this.getType(pos); ++ } ++ return DEFAULT_BLOCK_STATE; ++ } ++ ++ default Fluid getFluidStateIfLoaded(BlockPosition pos) { ++ if (this.isLoaded(pos)) { ++ return this.getFluid(pos); ++ } ++ return DEFAULT_FLUID_STATE; ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/net/minecraft/server/level/EntityPlayer.java b/src/main/java/net/minecraft/server/level/EntityPlayer.java +index a33cbd6504fd0d5c5e50aa63009d70533c01f686..e4f8bb15201e89d0832e3bcdfcbc12667f51165b 100644 +--- a/src/main/java/net/minecraft/server/level/EntityPlayer.java ++++ b/src/main/java/net/minecraft/server/level/EntityPlayer.java +@@ -657,6 +657,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + } + + public void playerTick() { ++ if (!this.world.isLoaded(this.getChunkCoordinates())) return; // Yatopia - tic-tac unblocking ++ + try { + if (valid && !this.isSpectator() || this.world.isLoaded(this.getChunkCoordinates())) { // Paper - don't tick dead players that are not in the world currently (pending respawn) + super.tick(); +diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java +index 7cedb4aaf4a002945866a0ca56eb9b0e7ae28de6..e054e98e413ed02b76e63b29c52bd70a559ad15d 100644 +--- a/src/main/java/net/minecraft/server/level/WorldServer.java ++++ b/src/main/java/net/minecraft/server/level/WorldServer.java +@@ -176,8 +176,9 @@ import org.bukkit.event.weather.LightningStrikeEvent; + import org.bukkit.event.world.TimeSkipEvent; + // CraftBukkit end + import it.unimi.dsi.fastutil.ints.IntArrayList; // Tuinity ++import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia + +-public class WorldServer extends World implements GeneratorAccessSeed { ++public class WorldServer extends World implements GeneratorAccessSeed, NonBlockingWorldAccess { // Yatopia + + public static final BlockPosition a = new BlockPosition(100, 50, 0); + private static final Logger LOGGER = LogManager.getLogger(); +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 4492f2579da79bd58def922894f40422fbeaf54c..cd1f94e5c1c923ee9d8dd451406ac2bee360e9c3 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -142,9 +142,12 @@ import org.bukkit.event.entity.EntityPoseChangeEvent; + import org.bukkit.event.player.PlayerTeleportEvent; + import org.bukkit.plugin.PluginManager; + // CraftBukkit end ++import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia + + public abstract class Entity implements INamableTileEntity, ICommandListener, net.minecraft.server.KeyedObject { // Paper + ++ public boolean updateNeeded; // Yatopia ++ private boolean chunkPosUpdateRequested; // Yatopia + // CraftBukkit start + private static final int CURRENT_LEVEL = 2; + boolean preserveMotion = true; // Paper - keep initial motion on first setPositionRotation +@@ -772,6 +775,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + } + // Tuinity end - detailed watchdog information + public void move(EnumMoveType enummovetype, Vec3D vec3d) { ++ // Yatopia start - tic-tacs unblocking ++ BlockPosition pos = this.getChunkCoordinates(); ++ if (!this.world.isLoaded(pos)) { ++ return; ++ } ++ // Yatopia end + // Tuinity start - detailed watchdog information + com.tuinity.tuinity.util.TickThread.ensureTickThread("Cannot move an entity off-main"); + synchronized (this.posLock) { +@@ -822,7 +831,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + this.v = vec3d.y != vec3d1.y; + this.onGround = this.v && vec3d.y < 0.0D; + BlockPosition blockposition = this.ap(); +- IBlockData iblockdata = this.world.getType(blockposition); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition); // Yatopia + + this.a(vec3d1.y, this.onGround, iblockdata, blockposition); + Vec3D vec3d2 = this.getMot(); +@@ -936,9 +945,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + int k = MathHelper.floor(this.loc.z); + BlockPosition blockposition = new BlockPosition(i, j, k); + +- if (this.world.getType(blockposition).isAir()) { ++ if (this.world.getBlockStateIfLoaded(blockposition).isAir()) { // Yatopia + BlockPosition blockposition1 = blockposition.down(); +- IBlockData iblockdata = this.world.getType(blockposition1); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition1); // Yatopia + Block block = iblockdata.getBlock(); + + if (block.a((Tag) TagsBlock.FENCES) || block.a((Tag) TagsBlock.WALLS) || block instanceof BlockFenceGate) { +@@ -950,17 +959,21 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + } + + protected float getBlockJumpFactor() { +- float f = this.world.getType(this.getChunkCoordinates()).getBlock().getJumpFactor(); +- float f1 = this.world.getType(this.as()).getBlock().getJumpFactor(); ++ // Yatopia start - tic-tacs unblocking ++ float f = this.world.getBlockStateIfLoaded(this.getChunkCoordinates()).getBlock().getJumpFactor(); ++ float f1 = this.world.getBlockStateIfLoaded(this.as()).getBlock().getJumpFactor(); ++ // Yatopia end + + return (double) f == 1.0D ? f1 : f; + } + + protected float getBlockSpeedFactor() { +- Block block = this.world.getType(this.getChunkCoordinates()).getBlock(); ++ // Yatopia start - tic-tacs unblocking ++ Block block = this.world.getBlockStateIfLoaded(this.getChunkCoordinates()).getBlock(); + float f = block.getSpeedFactor(); + +- return block != Blocks.WATER && block != Blocks.BUBBLE_COLUMN ? ((double) f == 1.0D ? this.world.getType(this.as()).getBlock().getSpeedFactor() : f) : f; ++ return block != Blocks.WATER && block != Blocks.BUBBLE_COLUMN ? ((double) f == 1.0D ? this.world.getBlockStateIfLoaded(this.as()).getBlock().getSpeedFactor() : f) : f; ++ // Yatopia end + } + + protected BlockPosition as() { +@@ -1305,7 +1318,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + for (int i = blockposition.getX(); i <= blockposition1.getX(); ++i) { + // Tuinity end - reorder iteration to more cache aware + blockposition_mutableblockposition.d(i, j, k); +- IBlockData iblockdata = this.world.getType(blockposition_mutableblockposition); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition_mutableblockposition); + + // Tuinity start - move fire checking in here - reuse getType from this method + if (checkFire) { +@@ -1341,7 +1354,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + + protected void b(BlockPosition blockposition, IBlockData iblockdata) { + if (!iblockdata.getMaterial().isLiquid()) { +- IBlockData iblockdata1 = this.world.getType(blockposition.up()); ++ IBlockData iblockdata1 = this.world.getBlockStateIfLoaded(blockposition.up()); // Yatopia + SoundEffectType soundeffecttype = iblockdata1.a(Blocks.SNOW) ? iblockdata1.getStepSound() : iblockdata.getStepSound(); + + this.playSound(soundeffecttype.getStepSound(), soundeffecttype.getVolume() * 0.15F, soundeffecttype.getPitch()); +@@ -1432,7 +1445,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + + public final boolean isInBubbleColumn() { return k(); } // Paper - OBFHELPER + private boolean k() { +- return this.world.getType(this.getChunkCoordinates()).a(Blocks.BUBBLE_COLUMN); ++ return this.world.getBlockStateIfLoaded(this.getChunkCoordinates()).a(Blocks.BUBBLE_COLUMN); // Yatopia + } + + public boolean isInWaterOrRain() { +@@ -1503,7 +1516,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + } + + BlockPosition blockposition = new BlockPosition(this.locX(), d0, this.locZ()); +- Fluid fluid = this.world.getFluid(blockposition); ++ Fluid fluid = this.world.getFluidStateIfLoaded(blockposition); // Yatopia + Iterator iterator = TagsFluid.b().iterator(); + + Tag tag; +@@ -1561,7 +1574,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + } + + protected IBlockData aN() { +- return this.world.getType(this.ap()); ++ return this.world.getBlockStateIfLoaded(this.ap()); // Yatopia + } + + public boolean aO() { +@@ -1573,7 +1586,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + int j = MathHelper.floor(this.locY() - 0.20000000298023224D); + int k = MathHelper.floor(this.locZ()); + BlockPosition blockposition = new BlockPosition(i, j, k); +- IBlockData iblockdata = this.world.getType(blockposition); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition); // Yatopia + + if (iblockdata.h() != EnumRenderType.INVISIBLE) { + Vec3D vec3d = this.getMot(); +@@ -2827,7 +2840,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + EnumDirection enumdirection1 = aenumdirection[j]; + + blockposition_mutableblockposition.a((BaseBlockPosition) blockposition, enumdirection1); +- if (!this.world.getType(blockposition_mutableblockposition).r(this.world, blockposition_mutableblockposition)) { ++ if (!this.world.getBlockStateIfLoaded(blockposition_mutableblockposition).r(this.world, blockposition_mutableblockposition)) { // Yatopia + double d4 = vec3d.a(enumdirection1.n()); + double d5 = enumdirection1.e() == EnumDirection.EnumAxisDirection.POSITIVE ? 1.0D - d4 : d4; + +@@ -3043,14 +3056,14 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + + return (ShapeDetectorShape) this.findOrCreatePortal(worldserver, blockposition, flag2, event.getSearchRadius(), event.getCanCreatePortal(), event.getCreationRadius()).map((blockutil_rectangle) -> { + // CraftBukkit end +- IBlockData iblockdata = this.world.getType(this.ac); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(this.ac); // Yatopia + EnumDirection.EnumAxis enumdirection_enumaxis; + Vec3D vec3d; + + if (iblockdata.b(BlockProperties.E)) { + enumdirection_enumaxis = (EnumDirection.EnumAxis) iblockdata.get(BlockProperties.E); + BlockUtil.Rectangle blockutil_rectangle1 = BlockUtil.a(this.ac, enumdirection_enumaxis, 21, EnumDirection.EnumAxis.Y, 21, (blockposition1) -> { +- return this.world.getType(blockposition1) == iblockdata; ++ return this.world.getBlockStateIfLoaded(blockposition1) == iblockdata; // Yatopia + }); + + vec3d = this.a(enumdirection_enumaxis, blockutil_rectangle1); +@@ -3417,6 +3430,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + } + + public boolean cl() { ++ if (!this.updateNeeded) this.chunkPosUpdateRequested = true; // Yatopia + boolean flag = this.au; + + this.au = false; +@@ -3620,7 +3634,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + for (int i2 = k; i2 < l; ++i2) { + for (int j2 = i1; j2 < j1; ++j2) { + blockposition_mutableblockposition.d(l1, i2, j2); +- Fluid fluid = this.world.getFluid(blockposition_mutableblockposition); ++ Fluid fluid = this.world.getFluidStateIfLoaded(blockposition_mutableblockposition); // Yatopia + + if (fluid.a(tag)) { + double d2 = (double) ((float) i2 + fluid.getHeight(this.world, blockposition_mutableblockposition)); +diff --git a/src/main/java/net/minecraft/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java +index fb62d5bfe188a7ae82a038454cbf0db10de23dda..2f5960a85b3599f369ad334f3b3a0bae8fad8ecf 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityLiving.java ++++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java +@@ -140,6 +140,7 @@ import org.bukkit.event.entity.EntityTeleportEvent; + import org.bukkit.event.player.PlayerItemConsumeEvent; + // CraftBukkit end + ++import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia + + public abstract class EntityLiving extends Entity { + +@@ -393,7 +394,7 @@ public abstract class EntityLiving extends Entity { + boolean flag1 = flag && ((EntityHuman) this).abilities.isInvulnerable; + + if (this.isAlive()) { +- if (this.a((Tag) TagsFluid.WATER) && !this.world.getType(new BlockPosition(this.locX(), this.getHeadY(), this.locZ())).a(Blocks.BUBBLE_COLUMN)) { ++ if (this.a((Tag) TagsFluid.WATER) && !this.world.getBlockStateIfLoaded(new BlockPosition(this.locX(), this.getHeadY(), this.locZ())).a(Blocks.BUBBLE_COLUMN)) { // Yatopia + if (!this.canBreatheUnderwater() && !MobEffectUtil.c(this) && !flag1) { // Paper - use OBFHELPER so it can be overridden + this.setAirTicks(this.l(this.getAirTicks())); + if (this.getAirTicks() == -this.world.purpurConfig.drowningDamageInterval) { // Purpur +@@ -486,7 +487,7 @@ public abstract class EntityLiving extends Entity { + } + + protected boolean cP() { +- return this.world.getType(this.as()).a((Tag) TagsBlock.SOUL_SPEED_BLOCKS); ++ return this.world.getBlockStateIfLoaded(this.as()).a((Tag) TagsBlock.SOUL_SPEED_BLOCKS); // Yatopia + } + + @Override +@@ -534,6 +535,8 @@ public abstract class EntityLiving extends Entity { + } + + protected void c(BlockPosition blockposition) { ++ if (!this.world.isLoaded(blockposition)) return; // Yatopia ++ + int i = EnchantmentManager.a(Enchantments.FROST_WALKER, this); + + if (i > 0) { +@@ -1580,7 +1583,7 @@ public abstract class EntityLiving extends Entity { + BlockPosition blockposition = this.getChunkCoordinates(); + IBlockData iblockdata = Blocks.WITHER_ROSE.getBlockData(); + +- if (this.world.getType(blockposition).isAir() && iblockdata.canPlace(this.world, blockposition)) { ++ if (this.world.getBlockStateIfLoaded(blockposition).isAir() && iblockdata.canPlace(this.world, blockposition)) { // Yatopia + this.world.setTypeAndData(blockposition, iblockdata, 3); + flag = true; + } +@@ -1772,12 +1775,12 @@ public abstract class EntityLiving extends Entity { + } + + public IBlockData ds() { +- return this.world.getType(this.getChunkCoordinates()); ++ return this.world.getBlockStateIfLoaded(this.getChunkCoordinates()); // Yatopia + } + + private boolean c(BlockPosition blockposition, IBlockData iblockdata) { + if ((Boolean) iblockdata.get(BlockTrapdoor.OPEN)) { +- IBlockData iblockdata1 = this.world.getType(blockposition.down()); ++ IBlockData iblockdata1 = this.world.getBlockStateIfLoaded(blockposition.down()); // Yatopia + + if (iblockdata1.a(Blocks.LADDER) && iblockdata1.get(BlockLadder.FACING) == iblockdata.get(BlockTrapdoor.FACING)) { + return true; +@@ -1824,7 +1827,7 @@ public abstract class EntityLiving extends Entity { + int i = MathHelper.floor(this.locX()); + int j = MathHelper.floor(this.locY() - 0.20000000298023224D); + int k = MathHelper.floor(this.locZ()); +- IBlockData iblockdata = this.world.getType(new BlockPosition(i, j, k)); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(new BlockPosition(i, j, k)); // Yatopia + + if (!iblockdata.isAir()) { + SoundEffectType soundeffecttype = iblockdata.getStepSound(); +@@ -2315,7 +2318,7 @@ public abstract class EntityLiving extends Entity { + private void a(Entity entity) { + Vec3D vec3d; + +- if (!entity.dead && !this.world.getType(entity.getChunkCoordinates()).getBlock().a((Tag) TagsBlock.PORTALS)) { ++ if (!entity.dead && !this.world.getBlockStateIfLoaded(entity.getChunkCoordinates()).getBlock().a((Tag) TagsBlock.PORTALS)) { // Yatopia + vec3d = entity.b(this); + } else { + vec3d = new Vec3D(entity.locX(), entity.locY() + (double) entity.getHeight(), entity.locZ()); +@@ -2369,7 +2372,7 @@ public abstract class EntityLiving extends Entity { + this.fallDistance = 0.0F; + } + +- Fluid fluid = this.world.getFluid(this.getChunkCoordinates()); ++ Fluid fluid = this.world.getFluidStateIfLoaded(this.getChunkCoordinates()); // Yatopia + double d1; + float f; + +@@ -2485,7 +2488,7 @@ public abstract class EntityLiving extends Entity { + } + } else { + BlockPosition blockposition = this.as(); +- float f5 = this.world.getType(blockposition).getBlock().getFrictionFactor(); ++ float f5 = this.world.getBlockStateIfLoaded(blockposition).getBlock().getFrictionFactor(); // Yatopia + + f = this.onGround ? f5 * 0.91F : 0.91F; + Vec3D vec3d6 = this.a(vec3d, f5); +@@ -3546,7 +3549,7 @@ public abstract class EntityLiving extends Entity { + + while (!flag2 && blockposition.getY() > 0) { + BlockPosition blockposition1 = blockposition.down(); +- IBlockData iblockdata = world.getType(blockposition1); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition1); // Yatopia + + if (iblockdata.getMaterial().isSolid()) { + flag2 = true; +@@ -3641,7 +3644,7 @@ public abstract class EntityLiving extends Entity { + this.stopRiding(); + } + +- IBlockData iblockdata = this.world.getType(blockposition); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition); // Yatopia + + if (iblockdata.getBlock() instanceof BlockBed) { + this.world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockBed.OCCUPIED, true), 3); +@@ -3660,7 +3663,7 @@ public abstract class EntityLiving extends Entity { + + private boolean x() { + return (Boolean) this.getBedPosition().map((blockposition) -> { +- return this.world.getType(blockposition).getBlock() instanceof BlockBed; ++ return this.world.getBlockStateIfLoaded(blockposition).getBlock() instanceof BlockBed; // Yatopia + }).orElse(false); + } + +@@ -3670,7 +3673,7 @@ public abstract class EntityLiving extends Entity { + + this.world.getClass(); + optional.filter(world::isLoaded).ifPresent((blockposition) -> { +- IBlockData iblockdata = this.world.getType(blockposition); ++ IBlockData iblockdata = this.world.getBlockStateIfLoaded(blockposition); // Yatopia + + if (iblockdata.getBlock() instanceof BlockBed) { + this.world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockBed.OCCUPIED, false), 3); +diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java +index aed97af643b8ec9b6fd5a5f06ff4f364daf39742..f5badbe0dee5c40cf83a5d2993d27ed70ddd2c85 100644 +--- a/src/main/java/net/minecraft/world/level/World.java ++++ b/src/main/java/net/minecraft/world/level/World.java +@@ -96,7 +96,9 @@ import org.bukkit.craftbukkit.block.data.CraftBlockData; + import org.bukkit.event.block.BlockPhysicsEvent; + // CraftBukkit end + +-public abstract class World implements GeneratorAccess, AutoCloseable { ++import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia ++ ++public abstract class World implements GeneratorAccess, AutoCloseable, NonBlockingWorldAccess { // Yatopia + + protected static final Logger LOGGER = LogManager.getLogger(); + public static final Codec> f = MinecraftKey.a.xmap(ResourceKey.b(IRegistry.L), ResourceKey::a); +diff --git a/src/main/java/net/minecraft/world/level/material/FluidType.java b/src/main/java/net/minecraft/world/level/material/FluidType.java +index 6a60f53407db840150b84f4d2a709cc2e92362a4..bf3300607f9142486dc790a600d4c2ffa998d376 100644 +--- a/src/main/java/net/minecraft/world/level/material/FluidType.java ++++ b/src/main/java/net/minecraft/world/level/material/FluidType.java +@@ -38,6 +38,7 @@ public abstract class FluidType { + this.a = fluid; + } + ++ public final Fluid getFluidData() { return this.h(); } // Yatopia - OBFHELPER + public final Fluid h() { + return this.a; + } diff --git a/patches/server/0064-lithium-optimize-BlockPos.iterateOutwards-by-caching.patch b/patches/server/0064-lithium-optimize-BlockPos.iterateOutwards-by-caching.patch new file mode 100644 index 00000000..556b3556 --- /dev/null +++ b/patches/server/0064-lithium-optimize-BlockPos.iterateOutwards-by-caching.patch @@ -0,0 +1,196 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: 2No2Name <50278648+2No2Name@users.noreply.github.com> +Date: Mon, 15 Feb 2021 14:58:55 -0500 +Subject: [PATCH] lithium: optimize `BlockPos.iterateOutwards` by caching + offsets + +Code taken from: https://github.com/CaffeineMC/lithium-fabric/pull/123 by 2No2Name licenced under the LGPLv3 License + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java +new file mode 100644 +index 0000000000000000000000000000000000000000..50e9df6056f713b2f1eaf0cb4a23875e0c6c2e2c +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/IterateOutwardsCache.java +@@ -0,0 +1,72 @@ ++package me.jellysquid.mods.lithium.common.cached_blockpos_iteration; ++ ++import it.unimi.dsi.fastutil.longs.LongArrayList; ++import it.unimi.dsi.fastutil.longs.LongList; ++import net.minecraft.core.BlockPosition; ++ ++import java.util.Iterator; ++import java.util.Random; ++import java.util.concurrent.ConcurrentHashMap; ++ ++/** ++ * @author 2No2Name, original implemenation by SuperCoder7979 and Gegy1000 ++ */ ++public class IterateOutwardsCache { ++ //POS_ZERO must not be replaced with BlockPos.ORIGIN, otherwise iterateOutwards at BlockPos.ORIGIN will not use the cache ++ public static final BlockPosition POS_ZERO = new BlockPosition(0,0,0); ++ ++ ++ private final ConcurrentHashMap table; ++ private final int capacity; ++ private final Random random; ++ ++ public IterateOutwardsCache(int capacity) { ++ this.capacity = capacity; ++ this.table = new ConcurrentHashMap<>(31); ++ this.random = new Random(); ++ } ++ ++ private void fillPositionsWithIterateOutwards(LongList entry, int xRange, int yRange, int zRange) { ++ // Add all positions to the cached list ++ for (BlockPosition pos : BlockPosition.iterateOutwards(POS_ZERO, xRange, yRange, zRange)) { ++ entry.add(pos.asLong()); ++ } ++ } ++ ++ public LongList getOrCompute(int xRange, int yRange, int zRange) { ++ long key = BlockPosition.asLong(xRange, yRange, zRange); ++ ++ LongArrayList entry = this.table.get(key); ++ if (entry != null) { ++ return entry; ++ } ++ ++ // Cache miss: compute and store ++ entry = new LongArrayList(128); ++ ++ this.fillPositionsWithIterateOutwards(entry, xRange, yRange, zRange); ++ ++ //decrease the array size, as of now it won't be modified anymore anyways ++ entry.trim(); ++ ++ //this might overwrite an entry as the same entry could have been computed and added during this thread's computation ++ //we do not use computeIfAbsent, as it can delay other threads for too long ++ Object previousEntry = this.table.put(key, entry); ++ ++ ++ if (previousEntry == null && this.table.size() > this.capacity) { ++ //prevent a memory leak by randomly removing about 1/8th of the elements when the exceed the desired capacity is exceeded ++ final Iterator iterator = this.table.keySet().iterator(); ++ //prevent an unlikely infinite loop caused by another thread filling the table concurrently using counting ++ for (int i = -this.capacity; iterator.hasNext() && i < 5; i++) { ++ Long key2 = iterator.next(); ++ //random is not threadsafe, but it doesn't matter here, because we don't need quality random numbers ++ if (this.random.nextInt(8) == 0 && key2 != key) { ++ iterator.remove(); ++ } ++ } ++ } ++ ++ return entry; ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e2e4f7968a399b4641df07b2931fff6dbc85ddb3 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/cached_blockpos_iteration/LongList2BlockPosMutableIterable.java +@@ -0,0 +1,47 @@ ++package me.jellysquid.mods.lithium.common.cached_blockpos_iteration; ++ ++import it.unimi.dsi.fastutil.longs.LongIterator; ++import it.unimi.dsi.fastutil.longs.LongList; ++import net.minecraft.core.BlockPosition; ++ ++import java.util.Iterator; ++ ++/** ++ * @author 2No2Name ++ */ ++public class LongList2BlockPosMutableIterable implements Iterable { ++ ++ private final LongList positions; ++ private final int xOffset, yOffset, zOffset; ++ ++ public LongList2BlockPosMutableIterable(BlockPosition offset, LongList posList) { ++ this.xOffset = offset.getX(); ++ this.yOffset = offset.getY(); ++ this.zOffset = offset.getZ(); ++ this.positions = posList; ++ } ++ ++ @Override ++ public Iterator iterator() { ++ return new Iterator() { ++ ++ private final LongIterator it = LongList2BlockPosMutableIterable.this.positions.iterator(); ++ private final BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(); ++ ++ @Override ++ public boolean hasNext() { ++ return it.hasNext(); ++ } ++ ++ @Override ++ public BlockPosition next() { ++ long nextPos = this.it.nextLong(); ++ return this.pos.setValues( ++ LongList2BlockPosMutableIterable.this.xOffset + BlockPosition.unpackLongX(nextPos), ++ LongList2BlockPosMutableIterable.this.yOffset + BlockPosition.unpackLongY(nextPos), ++ LongList2BlockPosMutableIterable.this.zOffset + BlockPosition.unpackLongZ(nextPos)); ++ } ++ }; ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/net/minecraft/core/BlockPosition.java b/src/main/java/net/minecraft/core/BlockPosition.java +index 9c508cc41575175d442cd51d75706830351a24f2..df6df2ae3e30ada07c90a26b9615d939508d3246 100644 +--- a/src/main/java/net/minecraft/core/BlockPosition.java ++++ b/src/main/java/net/minecraft/core/BlockPosition.java +@@ -19,10 +19,16 @@ import net.minecraft.world.phys.Vec3D; + import org.apache.commons.lang3.Validate; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++import me.jellysquid.mods.lithium.common.cached_blockpos_iteration.IterateOutwardsCache; // Yatopia ++import me.jellysquid.mods.lithium.common.cached_blockpos_iteration.LongList2BlockPosMutableIterable; // Yatopia ++import it.unimi.dsi.fastutil.longs.LongList; // Yatopia + + @Immutable + public class BlockPosition extends BaseBlockPosition { + ++ private static final IterateOutwardsCache ITERATE_OUTWARDS_CACHE = new IterateOutwardsCache(50); // Yatopia ++ private static final LongList HOGLIN_PIGLIN_CACHE = ITERATE_OUTWARDS_CACHE.getOrCompute(8, 4, 8); // Yatopia ++ + public static final Codec a = Codec.INT_STREAM.comapFlatMap((intstream) -> { + return SystemUtils.a(intstream, 3).map((aint) -> { + return new BlockPosition(aint[0], aint[1], aint[2]); +@@ -78,14 +84,17 @@ public class BlockPosition extends BaseBlockPosition { + return a((int) (i >> 38) + j, (int) ((i << 52) >> 52) + k, (int) ((i << 26) >> 38) + l); // Paper - simplify/inline + } + ++ public static int unpackLongX(long i) { return BlockPosition.b(i); } // Yatopia - OBFHELPER + public static int b(long i) { + return (int) (i >> 38); // Paper - simplify/inline + } + ++ public static int unpackLongY(long i) { return BlockPosition.c(i); } // Yatopia - OBFHELPER + public static int c(long i) { + return (int) ((i << 52) >> 52); // Paper - simplify/inline + } + ++ public static int unpackLongZ(long i) { return BlockPosition.d(i); } // Yatopia - OBFHELPER + public static int d(long i) { + return (int) ((i << 26) >> 38); // Paper - simplify/inline + } +@@ -266,7 +275,15 @@ public class BlockPosition extends BaseBlockPosition { + }; + } + ++ public static Iterable iterateOutwards(BlockPosition blockposition, int p_i, int p_j, int p_k) { return BlockPosition.a(blockposition, p_i, p_j, p_k); } // Yatopia - OBFHELPER + public static Iterable a(BlockPosition blockposition, int p_i, int p_j, int p_k) { // Paper - decompile issues - variable name conflicts to inner class field refs ++ // Yatopia start - lithium: optimize `BlockPos.iterateOutwards` by caching offsets ++ if (blockposition != me.jellysquid.mods.lithium.common.cached_blockpos_iteration.IterateOutwardsCache.POS_ZERO) { ++ final LongList positions = p_i == 8 && p_j == 4 && p_k == 8 ? HOGLIN_PIGLIN_CACHE : ITERATE_OUTWARDS_CACHE.getOrCompute(p_i, p_j, p_j); ++ return new LongList2BlockPosMutableIterable(blockposition, positions); ++ } ++ // Yatopia end ++ + int l_decompiled = p_i + p_j + p_k; // Paper - decompile issues + int i1 = blockposition.getX(); + int j1 = blockposition.getY(); diff --git a/patches/server/0065-lithium-AI.patch b/patches/server/0065-lithium-AI.patch new file mode 100644 index 00000000..9ac9008b --- /dev/null +++ b/patches/server/0065-lithium-AI.patch @@ -0,0 +1,2154 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JellySquid +Date: Sun, 24 Jan 2021 12:17:19 +0100 +Subject: [PATCH] lithium AI + +Co-authored-by: Hugo Planque + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeCache.java b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeCache.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b97dc93ba3708811cd8a1409aa5992a548586ed0 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeCache.java +@@ -0,0 +1,92 @@ ++package me.jellysquid.mods.lithium.common.ai.pathing; ++ ++import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; ++import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; ++import net.minecraft.world.level.chunk.ChunkSection; ++import net.minecraft.world.level.block.state.IBlockData; ++import net.minecraft.world.level.pathfinder.PathType; ++ ++public class PathNodeCache { ++ /** ++ * A transient hash table of chunk sections and whether or not they contain dangerous block types. Used as a cache ++ * to avoid scanning for many neighbors when we know the chunk is free of dangers. This is only safe to use when ++ * we know the world is not going to be modified while it is active. ++ */ ++ private static final Reference2BooleanMap chunkNeighborDangerCache = new Reference2BooleanOpenHashMap<>(); ++ ++ /** ++ * True if the chunk danger cache is enabled and can be used. ++ */ ++ private static boolean dangerCacheEnabled = false; ++ ++ /** ++ * The previous chunk section that was queried for neighboring dangers. ++ */ ++ private static ChunkSection prevQueriedNeighborSectionKey; ++ ++ /** ++ * The result of the previous query belonging to {@link PathNodeCache#prevQueriedNeighborSectionKey}. ++ */ ++ private static boolean prevQueriedNeighborSectionResult; ++ ++ /** ++ * Enables the chunk danger cache. This should be called immediately before a controlled path-finding code path ++ * begins so that we can accelerate nearby danger checks. ++ */ ++ public static void enableChunkCache() { ++ dangerCacheEnabled = true; ++ } ++ ++ /** ++ * Disables and clears the chunk danger cache. This should be called immediately before path-finding ends so that ++ * block updates are reflected for future path-finding tasks. ++ */ ++ public static void disableChunkCache() { ++ dangerCacheEnabled = false; ++ chunkNeighborDangerCache.clear(); ++ ++ prevQueriedNeighborSectionKey = null; ++ prevQueriedNeighborSectionResult = false; ++ } ++ ++ private static boolean isChunkSectionDangerousNeighbor(ChunkSection section) { ++ return section.getBlocks() ++ .contains(state -> getNeighborPathNodeType(state) != PathType.OPEN); ++ } ++ ++ public static PathType getPathNodeType(IBlockData state) { ++ return state.getPathNodeType(); ++ } ++ ++ public static PathType getNeighborPathNodeType(IBlockData state) { ++ return state.getNeighborPathNodeType(); ++ } ++ ++ /** ++ * Returns whether or not a chunk section is free of dangers. This makes use of a caching layer to greatly ++ * accelerate neighbor danger checks when path-finding. ++ * ++ * @param section The chunk section to test for dangers ++ * @return True if this neighboring section is free of any dangers, otherwise false if it could ++ * potentially contain dangers ++ */ ++ public static boolean isSectionSafeAsNeighbor(ChunkSection section) { ++ // Empty sections can never contribute a danger ++ if (ChunkSection.isEmpty(section)) { ++ return true; ++ } ++ ++ // If the caching code path is disabled, the section must be assumed to potentially contain dangers ++ if (!dangerCacheEnabled) { ++ return false; ++ } ++ ++ if (prevQueriedNeighborSectionKey != section) { ++ prevQueriedNeighborSectionKey = section; ++ prevQueriedNeighborSectionResult = !chunkNeighborDangerCache.computeBooleanIfAbsent(section, ++ PathNodeCache::isChunkSectionDangerousNeighbor); ++ } ++ ++ return prevQueriedNeighborSectionResult; ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults.java b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4427f2171671896c978908b1c3d72b3f64f16a0b +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/ai/pathing/PathNodeDefaults.java +@@ -0,0 +1,110 @@ ++package me.jellysquid.mods.lithium.common.ai.pathing; ++ ++import net.minecraft.world.level.block.Block; ++import net.minecraft.world.level.block.BlockCampfire; ++import net.minecraft.world.level.block.BlockDoor; ++import net.minecraft.world.level.block.BlockFenceGate; ++import net.minecraft.world.level.block.BlockLeaves; ++import net.minecraft.world.level.block.BlockMinecartTrackAbstract; ++import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.material.Fluid; ++import net.minecraft.world.level.block.state.IBlockData; ++import net.minecraft.world.level.material.Material; ++import net.minecraft.world.level.pathfinder.PathType; ++import net.minecraft.tags.TagsBlock; ++import net.minecraft.tags.TagsFluid; ++ ++public class PathNodeDefaults { ++ public static PathType getNeighborNodeType(IBlockData state) { ++ if (state.isAir()) { ++ return PathType.OPEN; ++ } ++ ++ // [VanillaCopy] LandPathNodeMaker#getNodeTypeFromNeighbors ++ // Determine what kind of obstacle type this neighbor is ++ if (state.a(Blocks.CACTUS)) { ++ return PathType.DANGER_CACTUS; ++ } else if (state.a(Blocks.SWEET_BERRY_BUSH)) { ++ return PathType.DANGER_OTHER; ++ } else if (isFireDangerSource(state)) { ++ return PathType.DANGER_FIRE; ++ } else if (state.getFluid().a(TagsFluid.WATER)) { ++ return PathType.WATER_BORDER; ++ } else { ++ return PathType.OPEN; ++ } ++ } ++ ++ public static PathType getNodeType(IBlockData state) { ++ if (state.isAir()) { ++ return PathType.OPEN; ++ } ++ ++ Block block = state.getBlock(); ++ Material material = state.getMaterial(); ++ ++ if (state.hasTag(TagsBlock.TRAPDOORS) || state.a(Blocks.LILY_PAD)) { ++ return PathType.TRAPDOOR; ++ } ++ ++ if (state.a(Blocks.CACTUS)) { ++ return PathType.DAMAGE_CACTUS; ++ } ++ ++ if (state.a(Blocks.SWEET_BERRY_BUSH) || state.a(Blocks.STONECUTTER)) { ++ return PathType.DAMAGE_OTHER; ++ } ++ ++ if (state.a(Blocks.HONEY_BLOCK)) { ++ return PathType.STICKY_HONEY; ++ } ++ ++ if (state.a(Blocks.COCOA)) { ++ return PathType.COCOA; ++ } ++ ++ if (isFireDangerSource(state)) { ++ return PathType.DAMAGE_FIRE; ++ } ++ ++ if (BlockDoor.l(state) && !state.get(BlockDoor.OPEN)) { ++ return PathType.DOOR_WOOD_CLOSED; ++ } ++ ++ if ((block instanceof BlockDoor) && (material == Material.ORE) && !state.get(BlockDoor.OPEN)) { ++ return PathType.DOOR_IRON_CLOSED; ++ } ++ ++ if ((block instanceof BlockDoor) && state.get(BlockDoor.OPEN)) { ++ return PathType.DOOR_OPEN; ++ } ++ ++ if (block instanceof BlockMinecartTrackAbstract) { ++ return PathType.RAIL; ++ } ++ ++ if (block instanceof BlockLeaves) { ++ return PathType.LEAVES; ++ } ++ ++ if (block.a(TagsBlock.FENCES) || block.a(TagsBlock.WALLS) || ((block instanceof BlockFenceGate) && !state.get(BlockFenceGate.OPEN))) { ++ return PathType.FENCE; ++ } ++ ++ // Retrieve the fluid state from the block state to avoid a second lookup ++ Fluid fluid = state.getFluid(); ++ if (fluid == null) { ++ return PathType.OPEN; ++ } else if (fluid.a(TagsFluid.WATER)) { ++ return PathType.WATER; ++ } else if (fluid.a(TagsFluid.LAVA)) { ++ return PathType.LAVA; ++ } ++ ++ return PathType.OPEN; ++ } ++ ++ private static boolean isFireDangerSource(IBlockData blockState) { ++ return blockState.a(TagsBlock.FIRE) || blockState.a(Blocks.LAVA) || blockState.a(Blocks.MAGMA_BLOCK) || BlockCampfire.g(blockState); ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java +new file mode 100644 +index 0000000000000000000000000000000000000000..23ea99d0ec8622eadadc2073022e59c4aac8dc3a +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/EntityTrackerEngine.java +@@ -0,0 +1,254 @@ ++package me.jellysquid.mods.lithium.common.entity.tracker; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; ++import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListener; ++import net.minecraft.core.BlockPosition; ++import net.minecraft.world.entity.EntityLiving; ++import net.minecraft.core.SectionPosition; ++import net.minecraft.world.level.levelgen.structure.StructureBoundingBox; ++ ++import java.util.ArrayList; ++import java.util.List; ++import java.util.Set; ++ ++/** ++ * Tracks the entities within a world and provides notifications to listeners when a tracked entity enters or leaves a ++ * watched area. This removes the necessity to constantly poll the world for nearby entities each tick and generally ++ * provides a sizable boost to performance. ++ */ ++public class EntityTrackerEngine { ++ private final Long2ObjectOpenHashMap sections; ++ private final Reference2ReferenceOpenHashMap> sectionsByEntity; ++ ++ ++ public EntityTrackerEngine() { ++ this.sections = new Long2ObjectOpenHashMap<>(); ++ this.sectionsByEntity = new Reference2ReferenceOpenHashMap<>(); ++ } ++ ++ /** ++ * Called when an entity is added to the world. ++ */ ++ public void onEntityAdded(int x, int y, int z, EntityLiving entity) { ++ if (this.addEntity(x, y, z, entity)) { ++ this.addListener(x, y, z, entity.getListener()); ++ } ++ } ++ ++ /** ++ * Called when an entity is removed from the world. ++ */ ++ public void onEntityRemoved(int x, int y, int z, EntityLiving entity) { ++ if (this.removeEntity(x, y, z, entity)) { ++ this.removeListener(entity.getListener()); ++ } ++ } ++ ++ private boolean addEntity(int x, int y, int z, EntityLiving entity) { ++ return this.getOrCreateList(x, y, z).addTrackedEntity(entity); ++ } ++ ++ private boolean removeEntity(int x, int y, int z, EntityLiving entity) { ++ TrackedEntityList list = this.getList(x, y, z); ++ ++ if (list == null) { ++ return false; ++ } ++ ++ return list.removeTrackedEntity(entity); ++ } ++ ++ private void addListener(int x, int y, int z, NearbyEntityListener listener) { ++ int r = listener.getChunkRange(); ++ ++ if (r == 0) { ++ return; ++ } ++ ++ if (this.sectionsByEntity.containsKey(listener)) { ++ ++ throw new IllegalStateException(errorMessageAlreadyListening(this.sectionsByEntity, listener, SectionPosition.a(x, y, z))); ++ } ++ ++ int yMin = Math.max(0, y - r); ++ int yMax = Math.min(y + r, 15); ++ ++ List all = new ArrayList<>((2 * r + 1) * (yMax - yMin + 1) * (2 * r + 1)); ++ ++ for (int x2 = x - r; x2 <= x + r; x2++) { ++ for (int y2 = yMin; y2 <= yMax; y2++) { ++ for (int z2 = z - r; z2 <= z + r; z2++) { ++ TrackedEntityList list = this.getOrCreateList(x2, y2, z2); ++ list.addListener(listener); ++ ++ all.add(list); ++ } ++ } ++ } ++ ++ this.sectionsByEntity.put(listener, all); ++ } ++ ++ private void removeListener(NearbyEntityListener listener) { ++ int r = listener.getChunkRange(); ++ ++ if (r == 0) { ++ return; ++ } ++ ++ List all = this.sectionsByEntity.remove(listener); ++ ++ if (all != null) { ++ for (TrackedEntityList list : all) { ++ list.removeListener(listener); ++ } ++ } else { ++ throw new IllegalArgumentException("Entity listener not tracked:" + listener.toString()); ++ } ++ } ++ ++ // Faster implementation which avoids removing from/adding to every list twice on an entity move event ++ private void moveListener(int aX, int aY, int aZ, int bX, int bY, int bZ, NearbyEntityListener listener) { ++ int radius = listener.getChunkRange(); ++ ++ if (radius == 0) { ++ return; ++ } ++ ++ StructureBoundingBox before = new StructureBoundingBox(aX - radius, aY - radius, aZ - radius, aX + radius, aY + radius, aZ + radius); ++ StructureBoundingBox after = new StructureBoundingBox(aX - radius, aY - radius, aZ - radius, bX + radius, bY + radius, bZ + radius); ++ ++ StructureBoundingBox merged = new StructureBoundingBox(before); ++ merged.c(after); ++ ++ BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(); ++ ++ for (int x = merged.a; x <= merged.d; x++) { ++ for (int y = merged.b; y <= merged.e; y++) { ++ for (int z = merged.c; z <= merged.f; z++) { ++ pos.setValues(x, y, z); ++ ++ boolean leaving = before.hasPoint(pos); ++ boolean entering = after.hasPoint(pos); ++ ++ // Nothing to change ++ if (leaving == entering) { ++ continue; ++ } ++ ++ if (leaving) { ++ // The listener has left the chunk ++ TrackedEntityList list = this.getList(x, y, z); ++ ++ if (list == null) { ++ throw new IllegalStateException("Expected there to be a listener list while moving entity but there was none"); ++ } ++ ++ list.removeListener(listener); ++ } else { ++ // The listener has entered the chunk ++ TrackedEntityList list = this.getOrCreateList(x, y, z); ++ list.addListener(listener); ++ } ++ } ++ } ++ } ++ } ++ ++ private TrackedEntityList getOrCreateList(int x, int y, int z) { ++ return this.sections.computeIfAbsent(encode(x, y, z), TrackedEntityList::new); ++ } ++ ++ private TrackedEntityList getList(int x, int y, int z) { ++ return this.sections.get(encode(x, y, z)); ++ } ++ ++ private static long encode(int x, int y, int z) { ++ return SectionPosition.asLong(x, y, z); ++ } ++ ++ private static SectionPosition decode(long xyz) { ++ return SectionPosition.a(xyz); ++ } ++ ++ private class TrackedEntityList { ++ private final Set entities = new ReferenceOpenHashSet<>(); ++ private final Set listeners = new ReferenceOpenHashSet<>(); ++ ++ private final long key; ++ ++ private TrackedEntityList(long key) { ++ this.key = key; ++ } ++ ++ public void addListener(NearbyEntityListener listener) { ++ for (EntityLiving entity : this.entities) { ++ listener.onEntityEnteredRange(entity); ++ } ++ ++ this.listeners.add(listener); ++ } ++ ++ public void removeListener(NearbyEntityListener listener) { ++ if (this.listeners.remove(listener)) { ++ for (EntityLiving entity : this.entities) { ++ listener.onEntityLeftRange(entity); ++ } ++ ++ this.checkEmpty(); ++ } ++ } ++ ++ public boolean addTrackedEntity(EntityLiving entity) { ++ for (NearbyEntityListener listener : this.listeners) { ++ listener.onEntityEnteredRange(entity); ++ } ++ ++ return this.entities.add(entity); ++ } ++ ++ public boolean removeTrackedEntity(EntityLiving entity) { ++ boolean ret = this.entities.remove(entity); ++ ++ if (ret) { ++ for (NearbyEntityListener listener : this.listeners) { ++ listener.onEntityLeftRange(entity); ++ } ++ ++ this.checkEmpty(); ++ } ++ ++ return ret; ++ } ++ ++ private void checkEmpty() { ++ if (this.entities.isEmpty() && this.listeners.isEmpty()) { ++ EntityTrackerEngine.this.sections.remove(this.key); ++ } ++ } ++ } ++ ++ ++ private static String errorMessageAlreadyListening(Reference2ReferenceOpenHashMap> sectionsByEntity, NearbyEntityListener listener, SectionPosition newLocation) { ++ StringBuilder builder = new StringBuilder(); ++ builder.append("Adding Entity listener a second time: ").append(listener.toString()); ++ builder.append("\n"); ++ builder.append(" wants to listen at: ").append(newLocation.toString()); ++ builder.append(" with cube radius: ").append(listener.getChunkRange()); ++ builder.append("\n"); ++ builder.append(" but was already listening at chunk sections: "); ++ String[] comma = new String[]{""}; ++ if (sectionsByEntity.get(listener) == null) { ++ builder.append("null"); ++ } else { ++ sectionsByEntity.get(listener).forEach(a -> { ++ builder.append(comma[0]); ++ builder.append(decode(a.key).toString()); ++ comma[0] = ", "; ++ }); ++ } ++ return builder.toString(); ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f84366ad98333c2b17b838883d9a3889572bba63 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListener.java +@@ -0,0 +1,25 @@ ++package me.jellysquid.mods.lithium.common.entity.tracker.nearby; ++ ++import net.minecraft.world.entity.EntityLiving; ++ ++/** ++ * The main interface used to receive events from the ++ * {@link me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine} of a world. ++ */ ++public interface NearbyEntityListener { ++ /** ++ * Returns the range (in chunks) of this listener. This must never change during the lifetime of the listener. ++ * TODO: Allow entity listeners to change the radius they receive updates within ++ */ ++ int getChunkRange(); ++ ++ /** ++ * Called by the entity tracker when an entity enters the range of this listener. ++ */ ++ void onEntityEnteredRange(EntityLiving entity); ++ ++ /** ++ * Called by the entity tracker when an entity leaves the range of this listener or is removed from the world. ++ */ ++ void onEntityLeftRange(EntityLiving entity); ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a56b23f0fbc1f2e31a79aa8b47635fecdf2490c9 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityListenerMulti.java +@@ -0,0 +1,59 @@ ++package me.jellysquid.mods.lithium.common.entity.tracker.nearby; ++ ++import net.minecraft.world.entity.EntityLiving; ++ ++import java.util.ArrayList; ++import java.util.List; ++ ++/** ++ * Allows for multiple listeners on an entity to be grouped under one logical listener. No guarantees are made about the ++ * order of which each sub-listener will be notified. ++ */ ++public class NearbyEntityListenerMulti implements NearbyEntityListener { ++ private final List listeners = new ArrayList<>(); ++ ++ public void addListener(NearbyEntityListener listener) { ++ this.listeners.add(listener); ++ } ++ ++ public void removeListener(NearbyEntityListener listener) { ++ this.listeners.remove(listener); ++ } ++ ++ @Override ++ public int getChunkRange() { ++ int range = 0; ++ ++ for (NearbyEntityListener listener : this.listeners) { ++ range = Math.max(range, listener.getChunkRange()); ++ } ++ ++ return range; ++ } ++ ++ @Override ++ public void onEntityEnteredRange(EntityLiving entity) { ++ for (NearbyEntityListener listener : this.listeners) { ++ listener.onEntityEnteredRange(entity); ++ } ++ } ++ ++ @Override ++ public void onEntityLeftRange(EntityLiving entity) { ++ for (NearbyEntityListener listener : this.listeners) { ++ listener.onEntityLeftRange(entity); ++ } ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder sublisteners = new StringBuilder(); ++ String comma = ""; ++ for (NearbyEntityListener listener : this.listeners) { ++ sublisteners.append(comma).append(listener.toString()); ++ comma = ","; //trick to drop the first comma ++ } ++ ++ return super.toString() + " with sublisteners: [" + sublisteners + "]"; ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java +new file mode 100644 +index 0000000000000000000000000000000000000000..01f9a626e761dd8cc26216e316e3a39362dc463d +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/entity/tracker/nearby/NearbyEntityTracker.java +@@ -0,0 +1,94 @@ ++package me.jellysquid.mods.lithium.common.entity.tracker.nearby; ++ ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; ++import net.minecraft.world.phys.AxisAlignedBB; ++import net.minecraft.world.entity.EntityLiving; ++import net.minecraft.util.MathHelper; ++import net.minecraft.nbt.NBTTagCompound; ++import net.minecraft.world.entity.ai.targeting.PathfinderTargetCondition; ++ ++import java.util.Set; ++ ++/** ++ * Maintains a collection of all entities within the range of this listener. This allows AI goals to quickly ++ * assess nearby entities which match the provided class. ++ */ ++public class NearbyEntityTracker implements NearbyEntityListener { ++ private final Class clazz; ++ private final EntityLiving self; ++ ++ private final int rangeC; ++ private final float rangeSq; ++ ++ private final Set nearby = new ReferenceOpenHashSet<>(); ++ ++ public NearbyEntityTracker(Class clazz, EntityLiving self, float range) { ++ this.clazz = clazz; ++ this.self = self; ++ this.rangeSq = range * range; ++ this.rangeC = Math.max((MathHelper.f(range) + 15) >> 4, 1); ++ } ++ ++ @Override ++ public int getChunkRange() { ++ return this.rangeC; ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public void onEntityEnteredRange(EntityLiving entity) { ++ if (!this.clazz.isInstance(entity)) { ++ return; ++ } ++ ++ this.nearby.add((T) entity); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public void onEntityLeftRange(EntityLiving entity) { ++ if (this.nearby.isEmpty() || !this.clazz.isInstance(entity)) { ++ return; ++ } ++ ++ this.nearby.remove((T) entity); ++ } ++ ++ /** ++ * Gets the closest T (extends LivingEntity) to the center of this tracker that also intersects with the given box and meets the ++ * requirements of the targetPredicate. ++ * The result may be different from vanilla if there are multiple closest entities. ++ * ++ * @param box the box the entities have to intersect ++ * @param targetPredicate predicate the entity has to meet ++ * @return the closest Entity that meets all requirements (distance, box intersection, predicate, type T) ++ */ ++ public T getClosestEntity(AxisAlignedBB box, PathfinderTargetCondition targetPredicate) { ++ double x = this.self.locX(); ++ double y = this.self.locY(); ++ double z = this.self.locZ(); ++ ++ T nearest = null; ++ double nearestDistance = Double.POSITIVE_INFINITY; ++ ++ for (T entity : this.nearby) { ++ double distance = entity.getDistanceSquared(x, y, z); ++ ++ if (distance < nearestDistance && (box == null || box.intersects(entity.getBoundingBox())) && targetPredicate.test(this.self, entity)) { ++ nearest = entity; ++ nearestDistance = distance; ++ } ++ } ++ ++ if (nearestDistance <= this.rangeSq) { ++ return nearest; ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public String toString() { ++ return super.toString() + " for entity class: " + this.clazz.getName() + ", in rangeSq: " + this.rangeSq + ", around entity: " + this.self.toString() + " with NBT: " + this.self.save(new NBTTagCompound()); ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/Collector.java b/src/main/java/me/jellysquid/mods/lithium/common/util/Collector.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8323c05845cd652e5ea5dd4b71c388a1f7374bf6 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/util/Collector.java +@@ -0,0 +1,11 @@ ++package me.jellysquid.mods.lithium.common.util; ++ ++public interface Collector { ++ /** ++ * Collects the passed object and performs additional processing on it, returning a flag as to whether or not ++ * collection should continue. ++ * ++ * @return True if collection should continue, otherwise false. ++ */ ++ boolean collect(T obj); ++} +\ No newline at end of file +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningLong2ObjectOpenHashMap.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningLong2ObjectOpenHashMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a94a40dcc3502d29950e5e387d658232a0bf5552 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/ListeningLong2ObjectOpenHashMap.java +@@ -0,0 +1,46 @@ ++package me.jellysquid.mods.lithium.common.util.collections; ++ ++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++ ++/** ++ * An extension for {@link Long2ObjectOpenHashMap} which allows callbacks to be installed for when an item is added to ++ * or removed from the map. ++ */ ++public class ListeningLong2ObjectOpenHashMap extends Long2ObjectOpenHashMap { ++ private final Callback addCallback, removeCallback; ++ ++ public ListeningLong2ObjectOpenHashMap(Callback addCallback, Callback removeCallback) { ++ this.addCallback = addCallback; ++ this.removeCallback = removeCallback; ++ } ++ ++ @Override ++ public V put(long k, V v) { ++ V ret = super.put(k, v); ++ ++ if (ret != v) { ++ if (ret != null) { ++ this.removeCallback.apply(k, v); ++ } ++ ++ this.addCallback.apply(k, v); ++ } ++ ++ return ret; ++ } ++ ++ @Override ++ public V remove(long k) { ++ V ret = super.remove(k); ++ ++ if (ret != null) { ++ this.removeCallback.apply(k, ret); ++ } ++ ++ return ret; ++ } ++ ++ public interface Callback { ++ void apply(long key, V value); ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5959d66582342f614bdadb2a1ef163e4fff25341 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/WorldHelper.java +@@ -0,0 +1,18 @@ ++package me.jellysquid.mods.lithium.common.world; ++ ++import net.minecraft.core.BlockPosition; ++ ++public class WorldHelper { ++ ++ public static boolean areNeighborsWithinSameChunk(BlockPosition pos) { ++ int localX = pos.getX() & 15; ++ int localY = pos.getY() & 15; ++ int localZ = pos.getZ() & 15; ++ ++ return localX > 0 && localY > 0 && localZ > 0 && localX < 15 && localY < 15 && localZ < 15; ++ } ++ ++ public static boolean areAllNeighborsOutOfBounds(BlockPosition pos) { ++ return pos.getY() < -1 || pos.getY() > 256; ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestCollectors.java b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestCollectors.java +new file mode 100644 +index 0000000000000000000000000000000000000000..069b204764d04b126ac8ef30eae8f7e1234badf5 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestCollectors.java +@@ -0,0 +1,28 @@ ++package me.jellysquid.mods.lithium.common.world.interests; ++ ++import me.jellysquid.mods.lithium.common.util.Collector; ++import net.minecraft.core.BlockPosition; ++import net.minecraft.world.entity.ai.village.poi.VillagePlace; ++import net.minecraft.world.entity.ai.village.poi.VillagePlaceRecord; ++import net.minecraft.world.entity.ai.village.poi.VillagePlaceSection; ++import net.minecraft.world.entity.ai.village.poi.VillagePlaceType; ++ ++import java.util.function.Predicate; ++ ++public class PointOfInterestCollectors { ++ public static Collector collectAllWithinRadius(BlockPosition pos, double radius, Collector out) { ++ double radiusSq = radius * radius; ++ ++ return (point) -> { ++ if (point.getPosition().distanceSquared(pos) <= radiusSq) { ++ return out.collect(point); ++ } ++ ++ return true; ++ }; ++ } ++ ++ public static Collector collectAllMatching(Predicate predicate, VillagePlace.Occupancy status, Collector out) { ++ return set -> set.get(predicate, status, out); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestTypeHelper.java b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestTypeHelper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..06d7c8a5ae23ec8b55661b4129c7c88657d1b9bf +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/interests/PointOfInterestTypeHelper.java +@@ -0,0 +1,22 @@ ++package me.jellysquid.mods.lithium.common.world.interests; ++ ++import it.unimi.dsi.fastutil.objects.ObjectSet; ++import net.minecraft.world.level.chunk.ChunkSection; ++import net.minecraft.world.level.block.state.IBlockData; ++ ++public class PointOfInterestTypeHelper { ++ private static ObjectSet TYPES; ++ ++ public static void init(ObjectSet types) { ++ if (TYPES != null) { ++ throw new IllegalStateException("Already initialized"); ++ } ++ ++ TYPES = types; ++ } ++ ++ public static boolean shouldScan(ChunkSection section) { ++ return section.hasAny(TYPES::contains); ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/net/minecraft/core/SectionPosition.java b/src/main/java/net/minecraft/core/SectionPosition.java +index a206a729b3afa01bf591fa4da1e5c14902da4778..716f91246c4a45fd49af806afd1781f11ff3b346 100644 +--- a/src/main/java/net/minecraft/core/SectionPosition.java ++++ b/src/main/java/net/minecraft/core/SectionPosition.java +@@ -22,6 +22,7 @@ public class SectionPosition extends BaseBlockPosition { + return new SectionPosition(blockposition.getX() >> 4, blockposition.getY() >> 4, blockposition.getZ() >> 4); // Paper + } + ++ public static SectionPosition from(ChunkCoordIntPair chunkCoordIntPair, int i){ return a(chunkCoordIntPair, i); } // Yatopia - OBFHELPER + public static SectionPosition a(ChunkCoordIntPair chunkcoordintpair, int i) { + return new SectionPosition(chunkcoordintpair.x, i, chunkcoordintpair.z); + } +@@ -94,14 +95,17 @@ public class SectionPosition extends BaseBlockPosition { + return i << 4; + } + ++ public static int unpackX(long i){ return b(i); } // Yatopia - OBFHELPER + public static int b(long i) { + return (int) (i << 0 >> 42); + } + ++ public static int unpackY(long i){ return c(i); } // Yatopia - OBFHELPER + public static int c(long i) { + return (int) (i << 44 >> 44); + } + ++ public static int unpackZ(long i){ return d(i); } // Yatopia - OBFHELPER + public static int d(long i) { + return (int) (i << 22 >> 42); + } +diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java +index e054e98e413ed02b76e63b29c52bd70a559ad15d..9ae4377bb9277ca15d2c56f3db880fe3d6435f4f 100644 +--- a/src/main/java/net/minecraft/server/level/WorldServer.java ++++ b/src/main/java/net/minecraft/server/level/WorldServer.java +@@ -177,6 +177,7 @@ import org.bukkit.event.world.TimeSkipEvent; + // CraftBukkit end + import it.unimi.dsi.fastutil.ints.IntArrayList; // Tuinity + import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia ++import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine; // Yatopia + + public class WorldServer extends World implements GeneratorAccessSeed, NonBlockingWorldAccess { // Yatopia + +@@ -2031,6 +2032,16 @@ public class WorldServer extends World implements GeneratorAccessSeed, NonBlocki + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + ++ if (entity instanceof EntityLiving) { // Yatopia start - Port lithium ++ ++ int chunkX = MathHelper.floor(entity.locX()) >> 4; ++ int chunkY = MathHelper.clamp(MathHelper.floor(entity.locY()) >> 4, 0, 15); ++ int chunkZ = MathHelper.floor(entity.locZ()) >> 4; ++ ++ EntityTrackerEngine tracker = this.getEntityTracker(); ++ tracker.onEntityRemoved(chunkX, chunkY, chunkZ, (EntityLiving) entity); ++ } // Yatopia End ++ + if (!(entity instanceof EntityPlayer)) { + if (false && this.tickingEntities) { // Tuinity + throw (IllegalStateException) SystemUtils.c((Throwable) (new IllegalStateException("Removing entity while ticking!"))); +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index cd1f94e5c1c923ee9d8dd451406ac2bee360e9c3..7befe4263a2d046922438e1a9853f2d8290ee230 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -177,6 +177,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + private CraftEntity bukkitEntity; + + public PlayerChunkMap.EntityTracker tracker; // Paper package private -> public ++ public PlayerChunkMap.EntityTracker getTracker() { return tracker; } // Yatopia + public boolean collisionLoadChunks = false; // Paper + public Throwable addedToWorldStack; // Paper - entity debug + public CraftEntity getBukkitEntity() { +diff --git a/src/main/java/net/minecraft/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java +index 2f5960a85b3599f369ad334f3b3a0bae8fad8ecf..bbe9eea55b18e1198916df29cbd922092b485eed 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityLiving.java ++++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java +@@ -141,9 +141,11 @@ import org.bukkit.event.player.PlayerItemConsumeEvent; + // CraftBukkit end + + import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia ++import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityListenerMulti; // Yatopia + + public abstract class EntityLiving extends Entity { + ++ private NearbyEntityListenerMulti tracker; // Yatopia - Port lithium + private static final UUID b = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); + private static final UUID c = UUID.fromString("87f46a96-686f-4796-b035-22e16ee9e038"); + private static final AttributeModifier d = new AttributeModifier(EntityLiving.b, "Sprinting speed boost", 0.30000001192092896D, AttributeModifier.Operation.MULTIPLY_TOTAL); +@@ -277,8 +279,14 @@ public abstract class EntityLiving extends Entity { + DynamicOpsNBT dynamicopsnbt = DynamicOpsNBT.a; + + this.bg = this.a(new Dynamic(dynamicopsnbt, dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), dynamicopsnbt.emptyMap())))); ++ this.tracker = new NearbyEntityListenerMulti(); // Yatopia - Port lithium + } + ++ // Yatopia start - Port lithium ++ public NearbyEntityListenerMulti getListener() { ++ return this.tracker; ++ } ++ // Yatopia end + protected void initAttributes() {} // Purpur + + public BehaviorController getBehaviorController() { +diff --git a/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java b/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java +index e38cdf81e3f3ba1a2ee86c3a202e5643dd9c0fd1..48524cdf7a1a5d4cff12b24b11dbea9935c85131 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java ++++ b/src/main/java/net/minecraft/world/entity/ai/BehaviorController.java +@@ -223,6 +223,7 @@ public class BehaviorController { + }).isPresent(); + } + ++ public boolean isMemoryInState(MemoryModuleType memorymoduletype, MemoryStatus memorystatus){ return a(memorymoduletype, memorystatus); } // Yatopia - OBFHELPER + public boolean a(MemoryModuleType memorymoduletype, MemoryStatus memorystatus) { + Optional> optional = (Optional) this.memories.get(memorymoduletype); + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +index ed3a3593b417131837341784b09cb3f9f76a44be..922f7bf57e65b0bab18b9044ff6467a2660aa992 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +@@ -1,5 +1,8 @@ + package net.minecraft.world.entity.ai.behavior; + ++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps; ++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; + import java.util.Iterator; + import java.util.Map; + import java.util.Map.Entry; +@@ -10,7 +13,7 @@ import net.minecraft.world.entity.ai.memory.MemoryStatus; + + public abstract class Behavior { + +- protected final Map, MemoryStatus> a; ++ protected final Map, MemoryStatus> a; protected final Map, MemoryStatus> getRequiredMemoryStates(){ return a; } // Yatopia - OBFHELPER + private Behavior.Status b; public final Behavior.Status getStatus() { return this.b; } // Tuinity - OBFHELPER + private long c; + co.aikar.timings.Timing timing; // Origami - behavior timing +@@ -29,7 +32,7 @@ public abstract class Behavior { + this.b = Behavior.Status.STOPPED; + this.d = i; + this.e = j; +- this.a = map; ++ this.a = new Reference2ObjectOpenHashMap<>(map); // Yatopia - Port lithium + String key = getClass().getSimpleName(); // Yatopia Compatible Fix + // Origami start - behavior timing + timing = co.aikar.timings.WorldTimingsHandler.getBehaviorTimings(key); +@@ -40,6 +43,7 @@ public abstract class Behavior { + return this.b; + } + ++ public final boolean tryStarting(WorldServer worldserver, E e0, long i){ return e(worldserver, e0, i); } // Yatopia - OBFHELPER + public final boolean e(WorldServer worldserver, E e0, long i) { + if (this.a(e0) && this.a(worldserver, e0)) { + this.b = Behavior.Status.RUNNING; +@@ -92,23 +96,17 @@ public abstract class Behavior { + } + + private boolean a(E e0) { +- Iterator iterator = this.a.entrySet().iterator(); ++ // Yatopia start - port lithium ++ Iterable, MemoryStatus>> iterable = ++ Reference2ObjectMaps.fastIterable((Reference2ObjectOpenHashMap, MemoryStatus>)this.getRequiredMemoryStates()); + +- MemoryModuleType memorymoduletype; +- MemoryStatus memorystatus; +- +- do { +- if (!iterator.hasNext()) { +- return true; ++ for (Reference2ObjectMap.Entry, MemoryStatus> entry : iterable) { ++ if (!e0.getBehaviorController().isMemoryInState(entry.getKey(), entry.getValue())) { ++ return false; + } ++ } + +- Entry, MemoryStatus> entry = (Entry) iterator.next(); +- +- memorymoduletype = (MemoryModuleType) entry.getKey(); +- memorystatus = (MemoryStatus) entry.getValue(); +- } while (e0.getBehaviorController().a(memorymoduletype, memorystatus)); +- +- return false; ++ return true; // Yatopia end + } + + public static enum Status { +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java b/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java +index c46cdffe3d877bff70b843766c8189eae06148ff..9cf43a86b573ad2c553956c081ae40820cbc7d5e 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/BehaviorGate.java +@@ -31,17 +31,15 @@ public class BehaviorGate extends Behavior { + + @Override + protected boolean b(WorldServer worldserver, E e0, long i) { +- // Tuinity start - remove streams +- List>> list = this.getList().getList(); +- for (int index = 0, len = list.size(); index < len; ++index) { +- Behavior behavior = list.get(index).getValue(); ++ // Tuinity start - remove streams // Yatopia start - Port lithium ++ for (Behavior behavior : this.getList()) { + if (behavior.getStatus() == Status.RUNNING && behavior.b(worldserver, e0, i)) { // copied from removed code, make sure to update + return true; + } + } + + return false; +- // Tuinity end - remove streams ++ // Tuinity end - remove streams // Yatopia start - Port lithium + } + + @Override +@@ -57,23 +55,19 @@ public class BehaviorGate extends Behavior { + + @Override + protected void d(WorldServer worldserver, E e0, long i) { +- // Tuinity start - remove streams +- List>> list = this.getList().getList(); +- for (int index = 0, len = list.size(); index < len; ++index) { +- Behavior behavior = list.get(index).getValue(); ++ // Tuinity start - remove streams // Yatopia start - Port lithium ++ for (Behavior behavior : this.getList()) { + if (behavior.getStatus() == Behavior.Status.RUNNING) { + behavior.f(worldserver, e0, i); // copied from removed code, make sure to update + } + } +- // Tuinity end - remove streams ++ // Tuinity end - remove streams // Yatopia end + } + + @Override + protected void c(WorldServer worldserver, E e0, long i) { +- // Tuinity start - remove streams +- List>> list = this.getList().getList(); +- for (int index = 0, len = list.size(); index < len; ++index) { +- Behavior behavior = list.get(index).getValue(); ++ // Tuinity start - remove streams// Yatopia start - Port lithium ++ for (Behavior behavior : this.getList()) { + if (behavior.getStatus() == Behavior.Status.RUNNING) { + behavior.g(worldserver, e0, i); // copied from removed code, make sure to update + } +@@ -81,7 +75,9 @@ public class BehaviorGate extends Behavior { + // Tuinity end - remove streams + BehaviorController behaviorcontroller = e0.getBehaviorController(); + +- this.b.forEach(behaviorcontroller::removeMemory); // Paper - decomp fix ++ for(MemoryModuleType moduleType : this.b){ ++ behaviorcontroller.removeMemory(moduleType); ++ } // Yatopia end + } + + @Override +@@ -98,29 +94,25 @@ public class BehaviorGate extends Behavior { + RUN_ONE { + @Override + public void a(WeightedList> weightedlist, WorldServer worldserver, E e0, long i) { +- // Tuinity start - remove streams +- List>> list = weightedlist.getList(); +- for (int index = 0, len = list.size(); index < len; ++index) { +- Behavior behavior = list.get(index).getValue(); +- if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.e(worldserver, e0, i)) { // copied from removed code, make sure to update ++ // Tuinity start - remove streams // Yatopia start - port lithium ++ for (Behavior behavior : weightedlist) { ++ if (behavior.getStatus() == Behavior.Status.STOPPED && behavior.tryStarting(worldserver, e0, i)) { // copied from removed code, make sure to update + break; + } + } +- // Tuinity end - remove streams ++ // Tuinity end - remove streams // Yatopia end + } + }, + TRY_ALL { + @Override + public void a(WeightedList> weightedlist, WorldServer worldserver, E e0, long i) { +- // Tuinity start - remove streams +- List>> list = weightedlist.getList(); +- for (int index = 0, len = list.size(); index < len; ++index) { +- Behavior behavior = list.get(index).getValue(); ++ // Tuinity start - remove streams // Yatopia start - port lithium ++ for (Behavior behavior : weightedlist) { + if (behavior.getStatus() == Behavior.Status.STOPPED) { +- behavior.e(worldserver, e0, i); // copied from removed code, make sure to update ++ behavior.tryStarting(worldserver, e0, i); // copied from removed code, make sure to update + } + } +- // Tuinity end - remove streams ++ // Tuinity end - remove streams // Yatopia end + } + }; + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java +index e2b5d6155bebdbf99b0850de7f9e1f5d342f9e2f..a3236e6359a2e72b4a41be4717780c20e2a31af3 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java +@@ -8,11 +8,12 @@ import com.mojang.serialization.Dynamic; + import com.mojang.serialization.DynamicOps; + + import java.util.Comparator; ++import java.util.Iterator; + import java.util.List; + import java.util.Random; + import java.util.stream.Stream; + +-public class WeightedList { ++public class WeightedList implements Iterable { // Yatopia - Port lithium + + protected final List> list; public final List> getList() { return this.list; } // Paper - decompile conflict // Tuinity - OBFHELPER + private final Random b; +@@ -116,4 +117,35 @@ public class WeightedList { + }; + } + } +-} ++ ++ ++ // Yatopia start - Port lithium ++ /** ++ * A wrapper type for an iterator over the entries of a {@link WeightedList} which de-references the contained ++ * values for consumers. ++ * ++ * @param The value type stored in each list entry ++ */ ++ class ListIterator implements Iterator { ++ private final Iterator> inner; ++ ++ public ListIterator(Iterator> inner) { ++ this.inner = inner; ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return this.inner.hasNext(); ++ } ++ ++ @Override ++ public U next() { ++ return this.inner.next().getValue(); ++ } ++ } ++ ++ @Override ++ public Iterator iterator() { ++ return new ListIterator<>(this.list.iterator()); ++ } ++} // Yatopia End +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java +index 35502bd2f7d9cebf5cfe1060e300a5032dbe6a5d..eea1a396f06e8feaa5637ba4e589a13169f514da 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalAvoidTarget.java +@@ -1,5 +1,7 @@ + package net.minecraft.world.entity.ai.goal; + ++import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityTracker; ++ + import java.util.EnumSet; + import java.util.function.Predicate; + import net.minecraft.world.entity.Entity; +@@ -25,6 +27,7 @@ public class PathfinderGoalAvoidTarget extends Pathfinde + protected final Predicate g; + protected final Predicate h; + private final PathfinderTargetCondition k; ++ private NearbyEntityTracker tracker; // Yatopia - Port Lithium + + public PathfinderGoalAvoidTarget(EntityCreature entitycreature, Class oclass, float f, double d0, double d1) { + this(entitycreature, oclass, entityliving -> true, f, d0, d1, IEntitySelector.e::test); // Purpur - decompile fix +@@ -41,6 +44,10 @@ public class PathfinderGoalAvoidTarget extends Pathfinde + this.e = entitycreature.getNavigation(); + this.a(EnumSet.of(PathfinderGoal.Type.MOVE)); + this.k = (new PathfinderTargetCondition()).a((double) f).a(predicate1.and(predicate)); ++ // Yatopia start - Port Lithium ++ this.tracker = new NearbyEntityTracker<>(oclass, entitycreature, f); ++ entitycreature.getListener().addListener(this.tracker); ++ // Yatopia end + } + + public PathfinderGoalAvoidTarget(EntityCreature entitycreature, Class oclass, float f, double d0, double d1, Predicate predicate) { +@@ -51,7 +58,7 @@ public class PathfinderGoalAvoidTarget extends Pathfinde + + @Override + public boolean a() { +- this.b = this.a.world.b(this.f, this.k, this.a, this.a.locX(), this.a.locY(), this.a.locZ(), this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c)); ++ this.b = this.tracker.getClosestEntity(this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c), this.k); // Yatopia - Port lithium + if (this.b == null) { + return false; + } else { +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java +index 3bcbad5e298cf05c1b41476a08a3a69cb7fdf79f..8b41274aa388a758bd8acf9637e2a19caf744c57 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/PathfinderGoalLookAtPlayer.java +@@ -1,5 +1,6 @@ + package net.minecraft.world.entity.ai.goal; + ++import me.jellysquid.mods.lithium.common.entity.tracker.nearby.NearbyEntityTracker; + import java.util.EnumSet; + import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.EntityInsentient; +@@ -10,6 +11,7 @@ import net.minecraft.world.entity.player.EntityHuman; + + public class PathfinderGoalLookAtPlayer extends PathfinderGoal { + ++ private NearbyEntityTracker tracker; // Yatopia - Port lithium + protected final EntityInsentient a; + protected Entity b; + protected final float c; +@@ -27,7 +29,7 @@ public class PathfinderGoalLookAtPlayer extends PathfinderGoal { + this.e = oclass; + this.c = f; + this.d = f1; +- this.a(EnumSet.of(PathfinderGoal.Type.LOOK)); ++ this.a(EnumSet.of(Type.LOOK)); // Yatopia - Port lithium + if (oclass == EntityHuman.class) { + this.f = (new PathfinderTargetCondition()).a((double) f).b().a().d().a((entityliving) -> { + return IEntitySelector.b(entityinsentient).test(entityliving); +@@ -35,7 +37,10 @@ public class PathfinderGoalLookAtPlayer extends PathfinderGoal { + } else { + this.f = (new PathfinderTargetCondition()).a((double) f).b().a().d(); + } +- ++ // Yatopia start - Port lithium ++ this.tracker = new NearbyEntityTracker<>(oclass, entityinsentient, f); ++ entityinsentient.getListener().addListener(this.tracker); ++ // Yatopia end + } + + @Override +@@ -48,9 +53,9 @@ public class PathfinderGoalLookAtPlayer extends PathfinderGoal { + } + + if (this.e == EntityHuman.class) { +- this.b = this.a.world.a(this.f, this.a, this.a.locX(), this.a.getHeadY(), this.a.locZ()); ++ this.b = this.tracker.getClosestEntity(null, this.f); // Yatopia - Port lithium + } else { +- this.b = this.a.world.b(this.e, this.f, this.a, this.a.locX(), this.a.getHeadY(), this.a.locZ(), this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c)); ++ this.b = this.tracker.getClosestEntity(this.a.getBoundingBox().grow((double) this.c, 3.0D, (double) this.c), this.f); // Yatopia - Port lithium + } + + return this.b != null; +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java +index 29cd71efe86eea2227f373c15c39dc530e9e8199..7dcd055b37bb7051127e10f5d191d23e0562b29e 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java +@@ -7,7 +7,12 @@ import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; + import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Tuinity + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + import it.unimi.dsi.fastutil.longs.LongSet; ++import me.jellysquid.mods.lithium.common.util.Collector; ++import me.jellysquid.mods.lithium.common.world.interests.PointOfInterestCollectors; ++import me.jellysquid.mods.lithium.common.world.interests.PointOfInterestTypeHelper; ++ + import java.io.File; ++import java.util.ArrayList; + import java.util.Collections; + import java.util.Comparator; + import java.util.List; +@@ -193,7 +198,7 @@ public class VillagePlace extends RegionFileSection { + + public long count(Predicate predicate, BlockPosition blockposition, int i, VillagePlace.Occupancy villageplace_occupancy) { return a(predicate, blockposition, i, villageplace_occupancy); } // Purpur - OBFHELPER + public long a(Predicate predicate, BlockPosition blockposition, int i, VillagePlace.Occupancy villageplace_occupancy) { +- return this.cList(predicate, blockposition, i, villageplace_occupancy).size(); // Yatopia ++ return this.getAllWithinCircle(predicate, blockposition, i, villageplace_occupancy).size(); // Yatopia + } + + public boolean a(VillagePlaceType villageplacetype, BlockPosition blockposition) { +@@ -236,6 +241,35 @@ public class VillagePlace extends RegionFileSection { + return ret; + } + ++ private List getAllWithinCircle(Predicate predicate, BlockPosition pos, int radius, Occupancy status) { ++ List points = new ArrayList<>(); ++ ++ this.collectWithinCircle(predicate, pos, radius, status, points::add); ++ ++ return points; ++ } ++ ++ private void collectWithinCircle(Predicate predicate, BlockPosition pos, int radius, Occupancy status, Collector collector) { ++ Collector filter = PointOfInterestCollectors.collectAllWithinRadius(pos, radius, collector); ++ Collector consumer = PointOfInterestCollectors.collectAllMatching(predicate, status, filter); ++ ++ int minChunkX = (pos.getX() - radius - 1) >> 4; ++ int minChunkZ = (pos.getZ() - radius - 1) >> 4; ++ ++ int maxChunkX = (pos.getX() + radius + 1) >> 4; ++ int maxChunkZ = (pos.getZ() + radius + 1) >> 4; ++ ++ // noinspection unchecked ++ ++ for (int x = minChunkX; x <= maxChunkX; x++) { ++ for (int z = minChunkZ; z <= maxChunkZ; z++) { ++ if (!this.collectWithinChunkColumn(x, z, consumer)) { ++ return; ++ } ++ } ++ } ++ } ++ + public java.util.List cListPositions(Predicate predicate, Predicate recordFilter, BlockPosition pos, int i, VillagePlace.Occupancy occupancy) { + int j = i * i; + java.util.List ret = new java.util.ArrayList<>(); +@@ -255,12 +289,12 @@ public class VillagePlace extends RegionFileSection { + }); + } + ++ /** ++ * @reason Retrieve all points of interest in one operation ++ * @author JellySquid ++ */ + public Stream a(Predicate predicate, ChunkCoordIntPair chunkcoordintpair, VillagePlace.Occupancy villageplace_occupancy) { +- return IntStream.range(0, 16).boxed().map((integer) -> { +- return this.d(SectionPosition.a(chunkcoordintpair, integer).s()); +- }).filter(Optional::isPresent).flatMap((optional) -> { +- return ((VillagePlaceSection) optional.get()).a(predicate, villageplace_occupancy); +- }); ++ return this.getWithinChunkColumn(chunkcoordintpair.x, chunkcoordintpair.z).flatMap((set) -> set.get(predicate, villageplace_occupancy)); + } + + // Yatopia start +@@ -273,12 +307,16 @@ public class VillagePlace extends RegionFileSection { + } + return ret; + } +- // Yatopia end ++ ++ /** ++ * @reason Retrieve all points of interest in one operation ++ * @author JellySquid ++ */ + public Stream a(Predicate predicate, Predicate predicate1, BlockPosition blockposition, int i, VillagePlace.Occupancy villageplace_occupancy) { + return this.c(predicate, blockposition, i, villageplace_occupancy).map(VillagePlaceRecord::f).filter(predicate1); + } + +- // Yatopia start ++ + public java.util.List bList(Predicate predicate, Predicate posFilter, BlockPosition pos, int i, VillagePlace.Occupancy occupancy) { + java.util.List ret = aList(predicate, posFilter, pos, i, occupancy); + ret.sort(Comparator.comparingDouble((pos1) -> pos1.distanceSquared(pos))); +@@ -404,24 +442,28 @@ public class VillagePlace extends RegionFileSection { + this.updateDistanceTracking(i); // Tuinity - move to new distance tracking util + } + ++ /** ++ * @reason Avoid Stream API ++ * @author Jellysquid ++ */ + public void a(ChunkCoordIntPair chunkcoordintpair, ChunkSection chunksection) { +- SectionPosition sectionposition = SectionPosition.a(chunkcoordintpair, chunksection.getYPosition() >> 4); ++ SectionPosition sectionPos = SectionPosition.from(chunkcoordintpair, chunksection.getYPosition() >> 4); + +- SystemUtils.a(this.d(sectionposition.s()), (villageplacesection) -> { +- villageplacesection.a((biconsumer) -> { +- if (a(chunksection)) { +- this.a(chunksection, sectionposition, biconsumer); +- } ++ VillagePlaceSection set = this.get(sectionPos.asLong()).orElse(null); + ++ if (set != null) { ++ set.refresh((consumer) -> { ++ if (PointOfInterestTypeHelper.shouldScan(chunksection)) { ++ this.scanAndPopulate(chunksection, sectionPos, consumer); ++ } + }); +- }, () -> { +- if (a(chunksection)) { +- VillagePlaceSection villageplacesection = (VillagePlaceSection) this.e(sectionposition.s()); ++ } else { ++ if (PointOfInterestTypeHelper.shouldScan(chunksection)) { ++ set = this.getOrCreate(sectionPos.asLong()); + +- this.a(chunksection, sectionposition, villageplacesection::a); ++ this.scanAndPopulate(chunksection, sectionPos, set::add); + } +- +- }); ++ } + } + + private static boolean a(ChunkSection chunksection) { +@@ -431,6 +473,7 @@ public class VillagePlace extends RegionFileSection { + return chunksection.a(set::contains); + } + ++ private void scanAndPopulate(ChunkSection chunksection, SectionPosition sectionposition, BiConsumer biconsumer){ a(chunksection, sectionposition, biconsumer); } // Yatopia - OBFHELPER + private void a(ChunkSection chunksection, SectionPosition sectionposition, BiConsumer biconsumer) { + sectionposition.tList().forEach((blockposition) -> { // Yatopia + IBlockData iblockdata = chunksection.getType(SectionPosition.b(blockposition.getX()), SectionPosition.b(blockposition.getY()), SectionPosition.b(blockposition.getZ())); +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java +index 866e9a434423702d2edaf9b52fa0e6219e13c2d7..640aee2b8afd66082cc867b14980f06d9811fc28 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceSection.java +@@ -26,6 +26,7 @@ import net.minecraft.core.SectionPosition; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + import org.apache.logging.log4j.util.Supplier; ++import me.jellysquid.mods.lithium.common.util.Collector; + + public class VillagePlaceSection { + +@@ -73,7 +74,28 @@ public class VillagePlaceSection { + } + return ret; + } ++ ++ public boolean get(Predicate type, VillagePlace.Occupancy status, Collector consumer) { ++ for (Map.Entry> entry : this.getData().entrySet()) { ++ if (!type.test(entry.getKey())) { ++ continue; ++ } ++ ++ for (VillagePlaceRecord poi : entry.getValue()) { ++ if (!status.getPredicate().test(poi)) { ++ continue; ++ } ++ ++ if (!consumer.collect(poi)) { ++ return false; ++ } ++ } ++ } ++ ++ return true; ++ } + // Yatopia end ++ public Stream get(Predicate predicate, VillagePlace.Occupancy villageplace_occupancy){ return a(predicate, villageplace_occupancy); } // Yatopia - OBFHELPER + public Stream a(Predicate predicate, VillagePlace.Occupancy villageplace_occupancy) { + return this.c.entrySet().stream().filter((entry) -> { + return predicate.test(entry.getKey()); +@@ -82,6 +104,7 @@ public class VillagePlaceSection { + }).filter(villageplace_occupancy.a()); + } + ++ public void add(BlockPosition blockPosition, VillagePlaceType villagePlaceType) { a(blockPosition, villagePlaceType); } // Yatopia - OBFHELPER + public void a(BlockPosition blockposition, VillagePlaceType villageplacetype) { + if (this.a(new VillagePlaceRecord(blockposition, villageplacetype, this.d))) { + VillagePlaceSection.LOGGER.debug("Added POI of type {} @ {}", new Supplier[]{() -> { +@@ -160,6 +183,7 @@ public class VillagePlaceSection { + return villageplacerecord != null ? Optional.of(villageplacerecord.g()) : Optional.empty(); + } + ++ public void refresh(Consumer> consumer){ a(consumer); } // Yatopia - OBFHELPER + public void a(Consumer> consumer) { + if (!this.e) { + Short2ObjectMap short2objectmap = new Short2ObjectOpenHashMap(this.b); +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java +index 4af526ecbed506161cb021ea320b0f21112d7bf0..4a7b3750f1b3ce3143053215dbca6da6eee052bd 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlaceType.java +@@ -4,7 +4,11 @@ import com.google.common.base.Suppliers; + import com.google.common.collect.ImmutableList; + import com.google.common.collect.ImmutableSet; + import com.google.common.collect.Maps; ++import it.unimi.dsi.fastutil.objects.ObjectArraySet; + import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; ++import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; ++import me.jellysquid.mods.lithium.common.world.interests.PointOfInterestTypeHelper; ++ + import java.util.Map; + import java.util.Optional; + import java.util.Set; +@@ -46,7 +50,8 @@ public class VillagePlaceType { + }).filter((iblockdata) -> { + return iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.HEAD; + }).collect(ImmutableSet.toImmutableSet()); +- private static final Map A = Maps.newHashMap(); ++ private static final Map A = new Reference2ReferenceOpenHashMap<>(); // Yatopia - Port Lithium ++ public static final Map getBlockStateToPointOfInterestType(){ return A; } // Yatopia - OBFHELPER + public static final VillagePlaceType c = a("unemployed", ImmutableSet.of(), 1, VillagePlaceType.a, 1); + public static final VillagePlaceType d = a("armorer", a(Blocks.BLAST_FURNACE), 1, 1); + public static final VillagePlaceType e = a("butcher", a(Blocks.SMOKER), 1, 1); +@@ -75,6 +80,12 @@ public class VillagePlaceType { + private final Predicate E; + private final int F; + ++ // Yatopia Start - Port lithium ++ static { ++ PointOfInterestTypeHelper.init(new ObjectArraySet<>(getBlockStateToPointOfInterestType().keySet())); ++ } ++ // Yatopia End ++ + private static Set a(Block block) { + return ImmutableSet.copyOf(block.getStates().a()); + } +diff --git a/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java b/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java +index 8eec32af12c69e1963dcd304a25ec4811b2f1f5a..e4bb472002934f749ff0e2f43744c73fcd74d3cc 100644 +--- a/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java ++++ b/src/main/java/net/minecraft/world/entity/raid/EntityRaider.java +@@ -47,9 +47,10 @@ import net.minecraft.world.phys.Vec3D; + + public abstract class EntityRaider extends EntityMonsterPatrolling { + ++ private static final ItemStack CACHED_OMINOUS_BANNER = Raid.getOminousBanner(); // Yatopia - Port lithium + protected static final DataWatcherObject c = DataWatcher.a(EntityRaider.class, DataWatcherRegistry.i); + private static final Predicate b = (entityitem) -> { +- return !entityitem.p() && entityitem.isAlive() && ItemStack.matches(entityitem.getItemStack(), Raid.s()); ++ return !entityitem.p() && entityitem.isAlive() && ItemStack.matches(entityitem.getItemStack(), CACHED_OMINOUS_BANNER); // Yatopia - Port lithium + }; + @Nullable + protected Raid d; +@@ -151,7 +152,7 @@ public abstract class EntityRaider extends EntityMonsterPatrolling { + } + } + +- if (!itemstack.isEmpty() && ItemStack.matches(itemstack, Raid.s()) && entityhuman != null) { ++ if (!itemstack.isEmpty() && ItemStack.matches(itemstack, CACHED_OMINOUS_BANNER) && entityhuman != null) { // Yatopia - Port lithium + MobEffect mobeffect = entityhuman.getEffect(MobEffects.BAD_OMEN); + byte b0 = 1; + int i; +@@ -242,7 +243,7 @@ public abstract class EntityRaider extends EntityMonsterPatrolling { + ItemStack itemstack = entityitem.getItemStack(); + boolean flag = this.fb() && this.fa().b(this.fc()) != null; + +- if (this.fb() && !flag && ItemStack.matches(itemstack, Raid.s())) { ++ if (this.fb() && !flag && ItemStack.matches(itemstack, CACHED_OMINOUS_BANNER)) { // Yatopia - Port lithium + EnumItemSlot enumitemslot = EnumItemSlot.HEAD; + ItemStack itemstack1 = this.getEquipment(enumitemslot); + double d0 = (double) this.e(enumitemslot); +@@ -535,7 +536,7 @@ public abstract class EntityRaider extends EntityMonsterPatrolling { + if ((!getRaider().world.purpurConfig.pillagerBypassMobGriefing && !getRaider().world.getGameRules().getBoolean(GameRules.MOB_GRIEFING)) || !getRaider().canPickupLoot()) return false; // Paper - respect game and entity rules for picking up items // Purpur + Raid raid = this.b.fa(); + +- if (this.b.fb() && !this.b.fa().a() && this.b.eN() && !ItemStack.matches(this.b.getEquipment(EnumItemSlot.HEAD), Raid.s())) { ++ if (this.b.fb() && !this.b.fa().a() && this.b.eN() && !ItemStack.matches(this.b.getEquipment(EnumItemSlot.HEAD), CACHED_OMINOUS_BANNER)) { // Yatopia - Port lithium + EntityRaider entityraider = raid.b(this.b.fc()); + + if (entityraider == null || !entityraider.isAlive()) { +diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java +index f9d03b6d11ad5ffbfe5be072e8631f046bcd1646..52def6de2225cf5ed70c807b74fb5c2ccd133503 100644 +--- a/src/main/java/net/minecraft/world/entity/raid/Raid.java ++++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java +@@ -227,8 +227,14 @@ public class Raid { + this.status = Raid.Status.STOPPED; + } + ++ private boolean isBarDirty; // Yatopia - Port lithium + public void o() { + if (!this.isStopped()) { ++ if (this.isBarDirty) { // Yatopia Start - Port lithium ++ this.bossBattle.setProgress(MathHelper.a(this.sumMobHealth() / this.totalHealth, 0.0F, 1.0F)); ++ ++ this.isBarDirty = false; ++ } // Yatopia End + if (this.status == Raid.Status.ONGOING) { + boolean flag = this.active; + +@@ -596,7 +602,7 @@ public class Raid { + } + + public void updateProgress() { +- this.bossBattle.setProgress(MathHelper.a(this.sumMobHealth() / this.totalHealth, 0.0F, 1.0F)); ++ this.isBarDirty = true; // Yatopia - Port lithium + } + + public float sumMobHealth() { +@@ -647,6 +653,7 @@ public class Raid { + this.world.getPersistentRaid().b(); + } + ++ public static ItemStack getOminousBanner(){ return s(); } // Yatopia - OBFHELPER + public static ItemStack s() { + ItemStack itemstack = new ItemStack(Items.WHITE_BANNER); + NBTTagCompound nbttagcompound = itemstack.a("BlockEntityTag"); +diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java +index f5badbe0dee5c40cf83a5d2993d27ed70ddd2c85..db2b1863b7f46be53839fb3e86870745fb7567fd 100644 +--- a/src/main/java/net/minecraft/world/level/World.java ++++ b/src/main/java/net/minecraft/world/level/World.java +@@ -97,6 +97,7 @@ import org.bukkit.event.block.BlockPhysicsEvent; + // CraftBukkit end + + import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia ++import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine; // Yatopia + + public abstract class World implements GeneratorAccess, AutoCloseable, NonBlockingWorldAccess { // Yatopia + +@@ -291,6 +292,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki + } + + // Tuinity end - optimise checkDespawn ++ private EntityTrackerEngine tracker; // Yatopia - Port lithium + + protected World(WorldDataMutable worlddatamutable, ResourceKey resourcekey, final DimensionManager dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env, java.util.concurrent.Executor executor) { // Paper + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.WorldDataServer) worlddatamutable).getName()); // Spigot +@@ -368,8 +370,14 @@ 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.tracker = new EntityTrackerEngine(); // Yatopia - Port lithium + } + ++ // Yatopia start - Port lithium ++ public EntityTrackerEngine getEntityTracker() { ++ return this.tracker; ++ } // Yatopia end ++ + // Paper start + // ret true if no collision + public final boolean checkEntityCollision(IBlockData data, Entity source, VoxelShapeCollision voxelshapedcollision, +@@ -536,6 +544,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki + return b(blockposition.getY()); + } + ++ public static boolean isOutOfBuildLimitVertically(int y){ return b(y); } // Yatopia - OBFHELPER + public static boolean b(int i) { + return i < 0 || i >= 256; + } +diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java +index eecb17e887bf0d1680a5fb5198a8b4246c14e548..3319c419a9eaf91224396c8b19eb057abd8094e6 100644 +--- a/src/main/java/net/minecraft/world/level/block/Block.java ++++ b/src/main/java/net/minecraft/world/level/block/Block.java +@@ -55,6 +55,7 @@ import net.minecraft.world.phys.shapes.VoxelShape; + import net.minecraft.world.phys.shapes.VoxelShapes; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeDefaults; + + public class Block extends BlockBase implements IMaterial { + +@@ -89,6 +90,17 @@ public class Block extends BlockBase implements IMaterial { + return timing; + } + // Paper end ++ ++ // Yatopia start - Port lithium ++ public net.minecraft.world.level.pathfinder.PathType getPathNodeType(IBlockData state) { ++ return PathNodeDefaults.getNodeType(state); ++ } ++ ++ public net.minecraft.world.level.pathfinder.PathType getNeighborPathNodeType(IBlockData state) { ++ return PathNodeDefaults.getNeighborNodeType(state); ++ } ++ // Yatopia end ++ + public Material getMaterial() { return material; } // Purpur - OBFHELPER + @Nullable + private String name; +diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBase.java b/src/main/java/net/minecraft/world/level/block/state/BlockBase.java +index b6de70c3630d96d0782a657c0389ce03839d8c43..611066964d11b2da7ab6dd59c6083c5c25c75de7 100644 +--- a/src/main/java/net/minecraft/world/level/block/state/BlockBase.java ++++ b/src/main/java/net/minecraft/world/level/block/state/BlockBase.java +@@ -3,6 +3,8 @@ package net.minecraft.world.level.block.state; + + import com.google.common.collect.ImmutableMap; + import com.mojang.serialization.MapCodec; ++import org.apache.commons.lang3.Validate; ++ + import java.util.Arrays; + import java.util.Collections; + import java.util.List; +@@ -63,6 +65,7 @@ import net.minecraft.world.phys.Vec3D; + import net.minecraft.world.phys.shapes.VoxelShape; + import net.minecraft.world.phys.shapes.VoxelShapeCollision; + import net.minecraft.world.phys.shapes.VoxelShapes; ++import net.minecraft.world.level.pathfinder.PathType; + + public abstract class BlockBase { + +@@ -349,6 +352,8 @@ public abstract class BlockBase { + + public abstract static class BlockData extends IBlockDataHolder { + ++ public net.minecraft.world.level.pathfinder.PathType pathNodeType = PathType.OPEN; // Yatopia - Port lithium ++ public net.minecraft.world.level.pathfinder.PathType pathNodeTypeNeighbor = PathType.OPEN; // Yatopia - Port lithium + private final int b; public final int getEmittedLight() { return this.b; } // Tuinity - OBFHELPER + private final boolean e; public final boolean isTransparentOnSomeFaces() { return this.e; } // Tuinity - OBFHELPER + private final boolean f; +@@ -420,6 +425,16 @@ public abstract class BlockBase { + } + // Tuinity end + ++ // Yatopia start - Port lithium ++ public net.minecraft.world.level.pathfinder.PathType getPathNodeType() { ++ return this.pathNodeType; ++ } ++ ++ public net.minecraft.world.level.pathfinder.PathType getNeighborPathNodeType() { ++ return this.pathNodeTypeNeighbor; ++ } ++ // Yatopia end ++ + public void a() { + this.fluid = this.getBlock().d(this.p()); // Paper - moved from getFluid() + this.isTicking = this.getBlock().isTicking(this.p()); // Paper - moved from isTicking() +@@ -429,6 +444,9 @@ public abstract class BlockBase { + this.shapeExceedsCube = this.a == null || this.a.c; // Tuinity - moved from actual method to here + this.staticPathType = null; // Tuinity - cache static path type + this.neighbourOverridePathType = null; // Tuinity - cache static path types ++ IBlockData state = this.getBlockData(); // Yatopia - Port lithium ++ this.pathNodeType = Validate.notNull(this.getBlock().getPathNodeType(state)); // Yatopia - Port lithium ++ this.pathNodeTypeNeighbor = Validate.notNull(this.getBlock().getNeighborPathNodeType(state)); // Yatopia - Port lithium + this.opacityIfCached = this.a == null || this.isConditionallyFullOpaque() ? -1 : this.a.getOpacity(); // Tuinity - cache opacity for light + // Tuinity start - optimise culling shape cache for light + if (this.a != null && this.a.getCullingShapeCache() != null) { +diff --git a/src/main/java/net/minecraft/world/level/chunk/Chunk.java b/src/main/java/net/minecraft/world/level/chunk/Chunk.java +index c07fb5ca761c0f2067bd103026ded618a8620947..cbe21ec18561c991aa27dd48d536c78e358d5b0e 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/Chunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/Chunk.java +@@ -68,6 +68,7 @@ import net.minecraft.world.level.material.FluidTypes; + import net.minecraft.world.phys.AxisAlignedBB; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++import net.minecraft.world.entity.EntityLiving; // Yatopia + + public class Chunk implements IChunkAccess { + +@@ -852,6 +853,11 @@ public class Chunk implements IChunkAccess { + if (!this.entitySlices[i].remove(entity)) { // Tuinity - optimise hard colliding entities // Tuinity - entities by class // Tuinity + return; + } ++ // Yatopia start - Port lithium ++ if (entity instanceof EntityLiving) { ++ this.world.getEntityTracker().onEntityAdded(entity.chunkX, entity.chunkY, entity.chunkZ, (EntityLiving) entity); ++ } ++ // Yatopia end + if (entity instanceof EntityItem) { + itemCounts[i]--; + } else if (entity instanceof IInventory) { +@@ -861,6 +867,11 @@ public class Chunk implements IChunkAccess { + this.markDirty(); // Paper + // Paper end + this.entities.remove(entity); // Paper ++ // Yatopia start - Port lithium ++ if (entity instanceof EntityLiving) { ++ this.world.getEntityTracker().onEntityRemoved(entity.chunkX, entity.chunkY, entity.chunkZ, (EntityLiving) entity); ++ } ++ // Yatopia end + } + + public final int getHighestBlockY(HeightMap.Type heightmap_type, int i, int j) { return this.getHighestBlock(heightmap_type, i, j) + 1; } // Paper - sort of an obfhelper, but without -1 +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java +index b5eb43174d2c2f34bb17bbcdb803aafe58989678..92f6301005031f4afab225e9fd01010eacd3ed26 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java +@@ -177,6 +177,7 @@ public class ChunkSection { + return 2 + this.blockIds.c(); + } + ++ public boolean hasAny(Predicate predicate) { return a(predicate); } // Yatopia - OBFHELPER + public boolean a(Predicate predicate) { + return this.blockIds.contains(predicate); + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java +index f70c14385c95763b5f270a6e2ce372cf047ba7bb..5968712468ad1bea4894d31d0d08b213735743e5 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java +@@ -13,10 +13,13 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; + import java.io.File; + import java.io.IOException; ++import java.util.BitSet; + import java.util.Map; ++import java.util.Objects; + import java.util.Optional; + import java.util.function.BooleanSupplier; + import java.util.function.Function; ++import java.util.stream.Stream; + import javax.annotation.Nullable; + import net.minecraft.SharedConstants; + import net.minecraft.SystemUtils; +@@ -29,27 +32,118 @@ import net.minecraft.world.level.ChunkCoordIntPair; + import net.minecraft.world.level.World; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++import me.jellysquid.mods.lithium.common.util.Collector; ++import me.jellysquid.mods.lithium.common.util.collections.ListeningLong2ObjectOpenHashMap; + + public class RegionFileSection extends RegionFileCache implements AutoCloseable { // Paper - nuke IOWorker + + private static final Logger LOGGER = LogManager.getLogger(); + // Paper - nuke IOWorker +- private final Long2ObjectMap> c = new Long2ObjectOpenHashMap(); protected final Long2ObjectMap> getDataBySection() { return this.c; } // Tuinity - OBFHELPER ++ private Long2ObjectMap> c = new Long2ObjectOpenHashMap(); protected final Long2ObjectMap> getDataBySection() { return this.c; } // Tuinity - OBFHELPER + public final LongLinkedOpenHashSet d = new LongLinkedOpenHashSet(); protected final LongLinkedOpenHashSet getDirtySections() { return this.d; } // Paper - private -> public // Tuinity - OBFHELPER + private final Function> e; + private final Function f; + private final DataFixer g; + private final DataFixTypes h; + ++ private Long2ObjectOpenHashMap columns; // Yatopia - Port lithium ++ + public RegionFileSection(File file, Function> function, Function function1, DataFixer datafixer, DataFixTypes datafixtypes, boolean flag) { + super(file, flag); // Paper - nuke IOWorker + this.e = function; + this.f = function1; + this.g = datafixer; + this.h = datafixtypes; ++ // Yatopia Start - Port lithium ++ this.columns = new Long2ObjectOpenHashMap<>(); ++ this.c = new ListeningLong2ObjectOpenHashMap<>(this::onEntryAdded, this::onEntryRemoved); + //this.b = new IOWorker(file, flag, file.getName()); // Paper - nuke IOWorker + } + ++ private void onEntryRemoved(long key, Optional value) { ++ // NO-OP... vanilla never removes anything, leaking entries. ++ // We might want to fix this. ++ } ++ ++ private void onEntryAdded(long key, Optional value) { ++ int y = SectionPosition.unpackY(key); ++ ++ // We only care about items belonging to a valid sub-chunk ++ if (y < 0 || y >= 16) { ++ return; ++ } ++ ++ int x = SectionPosition.unpackX(key); ++ int z = SectionPosition.unpackZ(key); ++ ++ long pos = ChunkCoordIntPair.pair(x, z); ++ ++ BitSet flags = this.columns.get(pos); ++ ++ if (flags == null) { ++ this.columns.put(pos, flags = new BitSet(16)); ++ } ++ ++ flags.set(y, value.isPresent()); ++ } ++ ++ public Stream getWithinChunkColumn(int chunkX, int chunkZ) { ++ BitSet flags = this.getCachedColumnInfo(chunkX, chunkZ); ++ ++ // No items are present in this column ++ if (flags.isEmpty()) { ++ return Stream.empty(); ++ } ++ ++ return flags.stream() ++ .mapToObj((chunkY) -> this.getDataBySection().get(SectionPosition.asLong(chunkX, chunkY, chunkZ)).orElse(null)) ++ .filter(Objects::nonNull); ++ } ++ ++ public boolean collectWithinChunkColumn(int chunkX, int chunkZ, Collector consumer) { ++ BitSet flags = this.getCachedColumnInfo(chunkX, chunkZ); ++ ++ // No items are present in this column ++ if (flags.isEmpty()) { ++ return true; ++ } ++ ++ for (int chunkY = flags.nextSetBit(0); chunkY >= 0; chunkY = flags.nextSetBit(chunkY + 1)) { ++ R obj = this.getDataBySection().get(SectionPosition.asLong(chunkX, chunkY, chunkZ)).orElse(null); ++ ++ if (obj != null && !consumer.collect(obj)) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ private BitSet getCachedColumnInfo(int chunkX, int chunkZ) { ++ long pos = ChunkCoordIntPair.pair(chunkX, chunkZ); ++ ++ BitSet flags = this.getColumnInfo(pos, false); ++ ++ if (flags != null) { ++ return flags; ++ } ++ ++ this.loadDataAt(new ChunkCoordIntPair(pos)); ++ ++ return this.getColumnInfo(pos, true); ++ } ++ ++ private BitSet getColumnInfo(long pos, boolean required) { ++ BitSet set = this.columns.get(pos); ++ ++ if (set == null && required) { ++ throw new NullPointerException("No data is present for column: " + new ChunkCoordIntPair(pos)); ++ } ++ ++ return set; ++ } ++ // Yatopia end ++ + protected void a(BooleanSupplier booleansupplier) { + while (!this.d.isEmpty() && booleansupplier.getAsBoolean()) { + ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(this.d.firstLong()).r(); // Paper - conflict here to avoid obfhelpers +@@ -92,6 +186,7 @@ public class RegionFileSection extends RegionFileCache implements AutoCloseab + return true; + } + // Tuinity end - actually unload POI data ++ protected Optional get(long i){ return d(i); } // Yatopia - OBFHELPER + + @Nullable public final Optional getIfLoaded(long value) { return this.c(value); } // Tuinity - OBFHELPER // Tuinity - OBFHELPER + @Nullable protected Optional c(long i) { // Tuinity - OBFHELPER +@@ -125,6 +220,7 @@ public class RegionFileSection extends RegionFileCache implements AutoCloseab + return World.b(SectionPosition.c(sectionposition.b())); + } + ++ protected R getOrCreate(long i){ return e(i); } // Yatopia - OBFHELPER + protected R e(long i) { + Optional optional = this.d(i); + +@@ -140,6 +236,7 @@ public class RegionFileSection extends RegionFileCache implements AutoCloseab + } + } + ++ public void loadDataAt(ChunkCoordIntPair chunkcoordintpair) { b(chunkcoordintpair); } // Yatopia - OBFHELPER + private void b(ChunkCoordIntPair chunkcoordintpair) { + // Paper start - load data in function + this.loadInData(chunkcoordintpair, this.c(chunkcoordintpair)); +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java b/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java +index 76e19f3a4ae988f6f3b59763d639fa5e084fa0bf..be67d9a931aec19105305f316959e97813c4feda 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/Pathfinder.java +@@ -3,6 +3,8 @@ package net.minecraft.world.level.pathfinder; + import com.google.common.collect.ImmutableSet; + import com.google.common.collect.Lists; + import com.google.common.collect.Sets; ++import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeCache; // Yatopia ++ + import java.util.Comparator; + import java.util.Iterator; + import java.util.List; +@@ -48,7 +50,7 @@ public class Pathfinder { + @Nullable + private PathEntity a(PathPoint pathpoint, List> list, float f, int i, float f1) { // Paper - optimize collection + //Set set = map.keySet(); // Paper +- ++ PathNodeCache.enableChunkCache(); // Yatopia - Port lithium + pathpoint.e = 0.0F; + pathpoint.f = this.a(pathpoint, list); // Paper - optimize collection + pathpoint.g = pathpoint.f; +diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java b/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java +index 9d08094165cf18d99116b5c721fff888f3cb42e2..b95804e73050dc7eb9786ca4bb5ea095904bad45 100644 +--- a/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java ++++ b/src/main/java/net/minecraft/world/level/pathfinder/PathfinderNormal.java +@@ -4,6 +4,9 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; + import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + import it.unimi.dsi.fastutil.objects.Object2BooleanMap; + import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; ++import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeCache; // Yatopia ++import me.jellysquid.mods.lithium.common.world.WorldHelper; // Yatopia ++ + import java.util.EnumSet; + import java.util.Iterator; + import javax.annotation.Nullable; +@@ -30,6 +33,10 @@ import net.minecraft.world.level.material.Material; + import net.minecraft.world.phys.AxisAlignedBB; + import net.minecraft.world.phys.Vec3D; + import net.minecraft.world.phys.shapes.VoxelShape; ++import net.minecraft.world.level.chunk.ChunkSection; // Yatopia ++import net.minecraft.world.level.chunk.IChunkAccess; // Yatopia ++import net.minecraft.world.level.World; // Yatopia ++import net.minecraft.world.level.ICollisionAccess; // Yatopia + + public class PathfinderNormal extends PathfinderAbstract { + +@@ -476,68 +483,111 @@ public class PathfinderNormal extends PathfinderAbstract { + return pathtype; + } + ++ /** ++ * @reason Use optimized implementation ++ * @author JellySquid ++ */ + public static PathType a(IBlockAccess iblockaccess, BlockPosition.MutableBlockPosition blockposition_mutableblockposition, PathType pathtype) { +- int i = blockposition_mutableblockposition.getX(); +- int j = blockposition_mutableblockposition.getY(); +- int k = blockposition_mutableblockposition.getZ(); ++ // Yatopia start - Port lithium ++ int x = blockposition_mutableblockposition.getX(); ++ int y = blockposition_mutableblockposition.getY(); ++ int z = blockposition_mutableblockposition.getZ(); ++ ++ ChunkSection section = null; ++ ++ // Check that all the block's neighbors are within the same chunk column. If so, we can isolate all our block ++ // reads to just one chunk and avoid hits against the server chunk manager. ++ if (iblockaccess instanceof ICollisionAccess && WorldHelper.areNeighborsWithinSameChunk(blockposition_mutableblockposition)) { ++ // If the y-coordinate is within bounds, we can cache the chunk section. Otherwise, the if statement to check ++ // if the cached chunk section was initialized will early-exit. ++ if (!World.b(y)) { ++ // This cast is always safe and is necessary to obtain direct references to chunk sections. ++ IChunkAccess chunk = (IChunkAccess) ((ICollisionAccess) iblockaccess).c(x >> 4, z >> 4); ++ ++ // If the chunk is absent, the cached section above will remain null, as there is no chunk section anyways. ++ // An empty chunk or section will never pose any danger sources, which will be caught later. ++ if (chunk != null) { ++ section = chunk.getSections()[y >> 4]; ++ } ++ } + +- for (int l = -1; l <= 1; ++l) { +- for (int i1 = -1; i1 <= 1; ++i1) { +- for (int j1 = -1; j1 <= 1; ++j1) { +- if (l != 0 || j1 != 0) { +- blockposition_mutableblockposition.d(i + l, j + i1, k + j1); +- // Paper start +- IBlockData iblockdata = iblockaccess.getTypeIfLoaded(blockposition_mutableblockposition); +- if (iblockdata == null) { +- pathtype = PathType.BLOCKED; +- } else { +- // Paper end +- // Tuinity start - reduce pathfinder branching +- if (iblockdata.neighbourOverridePathType == PathType.OPEN) { +- continue; +- } else if (iblockdata.neighbourOverridePathType != null) { +- return iblockdata.neighbourOverridePathType; +- } +- // Tuinity end - reduce pathfinder branching +- if (iblockdata.a(Blocks.CACTUS)) { +- return iblockdata.neighbourOverridePathType = PathType.DANGER_CACTUS; // Tuinity - reduce pathfinder branching +- } ++ // If we can guarantee that blocks won't be modified while the cache is active, try to see if the chunk ++ // section is empty or contains any dangerous blocks within the palette. If not, we can assume any checks ++ // against this chunk section will always fail, allowing us to fast-exit. ++ if (ChunkSection.isEmpty(section) || PathNodeCache.isSectionSafeAsNeighbor(section)) { ++ return pathtype; ++ } ++ } + +- if (iblockdata.a(Blocks.SWEET_BERRY_BUSH) || iblockdata.a(Blocks.STONECUTTER)) { // Purpur +- return iblockdata.neighbourOverridePathType = PathType.DANGER_OTHER; // Tuinity - reduce pathfinder branching +- } ++ int xStart = x - 1; ++ int yStart = y - 1; ++ int zStart = z - 1; ++ ++ int xEnd = x + 1; ++ int yEnd = y + 1; ++ int zEnd = z + 1; ++ ++ // Vanilla iteration order is XYZ ++ for (int adjX = xStart; adjX <= xEnd; adjX++) { ++ for (int adjY = yStart; adjY <= yEnd; adjY++) { ++ for (int adjZ = zStart; adjZ <= zEnd; adjZ++) { ++ // Skip the vertical column of the origin block ++ if (adjX == x && adjZ == z) { ++ continue; ++ } + +- if (a(iblockdata)) { +- return iblockdata.neighbourOverridePathType = PathType.DANGER_FIRE; // Tuinity - reduce pathfinder branching +- } ++ IBlockData state; + +- if (iblockdata.getFluid().a((Tag) TagsFluid.WATER)) { // Paper - remove another getType call +- return iblockdata.neighbourOverridePathType = PathType.WATER_BORDER; // Tuinity - reduce pathfinder branching +- } +- iblockdata.neighbourOverridePathType = PathType.OPEN; // Tuinity - reduce pathfinder branching +- } // Paper ++ // If we're not accessing blocks outside a given section, we can greatly accelerate block state ++ // retrieval by calling upon the cached chunk directly. ++ if (section != null) { ++ state = section.getType(adjX & 15, adjY & 15, adjZ & 15); ++ } else { ++ state = iblockaccess.getType(blockposition_mutableblockposition.setValues(adjX, adjY, adjZ)); ++ } ++ ++ // Ensure that the block isn't air first to avoid expensive hash table accesses ++ if (state.isAir()) { ++ continue; ++ } ++ ++ PathType neighborType = PathNodeCache.getNeighborPathNodeType(state); ++ ++ if (neighborType != PathType.OPEN) { ++ return neighborType; + } + } + } + } + +- return pathtype; ++ return pathtype; // Yatopia end + } + ++ /** ++ * @reason Use optimized implementation which avoids scanning blocks for dangers where possible ++ * @author JellySquid ++ */ + protected static PathType b(IBlockAccess iblockaccess, BlockPosition blockposition) { +- IBlockData iblockdata = iblockaccess.getTypeIfLoaded(blockposition); // Paper +- if (iblockdata == null) return PathType.BLOCKED; // Paper +- // Tuinity start - reduce pathfinder branches +- if (iblockdata.staticPathType != null) { +- return iblockdata.staticPathType; +- } +- if (iblockdata.getShapeCache() == null) { +- // while it might be called static, it might vary on shape! However, only a few blocks have variable shape. +- // So we rarely enter here. +- return getStaticTypeSlow(iblockaccess, blockposition, iblockdata); +- } else { +- return iblockdata.staticPathType = getStaticTypeSlow(iblockaccess, blockposition, iblockdata); ++ // Yatopia start - Port lithium ++ IBlockData blockState = iblockaccess.getType(blockposition); ++ if (blockState == null) return PathType.BLOCKED; ++ PathType type = PathNodeCache.getPathNodeType(blockState); ++ ++ // If the node type is open, it means that we were unable to determine a more specific type, so we need ++ // to check the fallback path. ++ if (type == PathType.OPEN) { ++ // This is only ever called in vanilla after all other possibilities are exhausted, but before fluid checks ++ // It should be safe to perform it last in actuality and take advantage of the cache for fluid types as well ++ // since fluids will always pass this check. ++ if (!blockState.a(iblockaccess, blockposition, PathMode.LAND)) { ++ return PathType.BLOCKED; ++ } ++ ++ // All checks succeed, this path node really is open! ++ return PathType.OPEN; + } ++ return type; ++ // Yatopia end + } + protected static PathType getStaticTypeSlow(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) { + // Tuinity end - reduce pathfinder branches +diff --git a/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java b/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java +index 292821f039d99a03ba4daeb3a941616ef5f6287e..9b7110a805b81906512acc3771217a56a102db67 100644 +--- a/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java ++++ b/src/main/java/net/pl3x/purpur/controller/ControllerLookWASD.java +@@ -42,7 +42,7 @@ public class ControllerLookWASD extends ControllerLook { + entity.setHeadRotation(entity.yaw); + entity.pitch = normalizePitch(pitch + pitchOffset); + +- entity.tracker.broadcast(new PacketPlayOutEntity ++ entity.getTracker().broadcast(new PacketPlayOutEntity // Yatopia + .PacketPlayOutRelEntityMoveLook(entity.getId(), + (short) 0, (short) 0, (short) 0, + (byte) MathHelper.d(entity.yaw * 256.0F / 360.0F), diff --git a/patches/server/0066-lithium-block.patch b/patches/server/0066-lithium-block.patch new file mode 100644 index 00000000..f9aaddd7 --- /dev/null +++ b/patches/server/0066-lithium-block.patch @@ -0,0 +1,232 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JellySquid +Date: Sun, 24 Jan 2021 16:57:03 +0100 +Subject: [PATCH] lithium block + +Co-authored-by: Hugo Planque + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/FinalObject.java b/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/FinalObject.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f78927e71258e859154cca8ba78d17130b0d82c1 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/util/tuples/FinalObject.java +@@ -0,0 +1,20 @@ ++package me.jellysquid.mods.lithium.common.util.tuples; ++ ++/** ++ * The purpose of this class is safe publication of the wrapped value. (JLS 17.5) ++ */ ++public class FinalObject { ++ private final T value; ++ ++ public FinalObject(T value) { ++ this.value = value; ++ } ++ ++ public FinalObject of(T value) { ++ return new FinalObject<>(value); ++ } ++ ++ public T getValue() { ++ return value; ++ } ++} +diff --git a/src/main/java/net/minecraft/core/EnumDirection.java b/src/main/java/net/minecraft/core/EnumDirection.java +index b8fe75b8c37ef1968519e69e078444b1a3c5c359..a825b17e454138e181926df3f2faa6d9d7af4055 100644 +--- a/src/main/java/net/minecraft/core/EnumDirection.java ++++ b/src/main/java/net/minecraft/core/EnumDirection.java +@@ -100,6 +100,7 @@ public enum EnumDirection implements INamable { + return new EnumDirection[]{enumdirection, enumdirection1, enumdirection2, enumdirection2.opposite(), enumdirection1.opposite(), enumdirection.opposite()}; + } + ++ public int get3DDataValue(){ return c(); } // Yatopia - OBFHELPER + public int c() { + return this.g; + } +diff --git a/src/main/java/net/minecraft/world/level/block/piston/TileEntityPiston.java b/src/main/java/net/minecraft/world/level/block/piston/TileEntityPiston.java +index 58c7a52612fe0f5c1e4ddacc0bf93cd81f1286b8..af2a8fdcaf5962334dcc20717f3a1ad14aebcbcb 100644 +--- a/src/main/java/net/minecraft/world/level/block/piston/TileEntityPiston.java ++++ b/src/main/java/net/minecraft/world/level/block/piston/TileEntityPiston.java +@@ -29,9 +29,9 @@ import net.minecraft.world.phys.shapes.VoxelShapes; + public class TileEntityPiston extends TileEntity implements ITickable { + + private IBlockData a; +- private EnumDirection b; +- private boolean c; +- private boolean g; ++ private EnumDirection b; public EnumDirection getFacing() { return b; } // Yatopia - OBFHELPER ++ private boolean c; public boolean isExtending() { return c; } // Yatopia - OBFHELPER ++ private boolean g; public boolean isSource() { return g; } // Yatopia - OBFHELPER + private static final ThreadLocal h = ThreadLocal.withInitial(() -> { + return null; + }); +@@ -40,6 +40,74 @@ public class TileEntityPiston extends TileEntity implements ITickable { + private long k; + private int l; + ++ // Yatopia start - Port Lithium ++ private static final VoxelShape[] PISTON_BASE_WITH_MOVING_HEAD_SHAPES = precomputePistonBaseWithMovingHeadShapes(); ++ /** ++ * We cache the offset and simplified VoxelShapes that are otherwise constructed on every call of getCollisionShape. ++ * For each offset direction and distance (6 directions, 2 distances each, and no direction with 0 distance) we ++ * store the offset and simplified VoxelShapes in the original VoxelShape when they are accessed the first time. ++ * We use safe publication, because both the Render and Server thread are using the cache. ++ * ++ * @param blockShape the original shape, must not be modified after passing it as an argument to this method ++ * @param offset the offset distance ++ * @param direction the offset direction ++ * @return blockShape offset and simplified ++ */ ++ private static VoxelShape getOffsetAndSimplified(VoxelShape blockShape, float offset, EnumDirection direction) { ++ VoxelShape offsetSimplifiedShape = blockShape.getOffsetSimplifiedShape(offset, direction); ++ if (offsetSimplifiedShape == null) { ++ //create the offset shape and store it for later use ++ offsetSimplifiedShape = blockShape.offset(direction.getAdjacentX() * offset, direction.getAdjacentY() * offset, direction.getAdjacentZ() * offset).simplify(); ++ blockShape.setShape(offset, direction, offsetSimplifiedShape); ++ } ++ return offsetSimplifiedShape; ++ } ++ ++ /** ++ * Precompute all 18 possible configurations for the merged piston base and head shape. ++ * ++ * @return The array of the merged VoxelShapes, indexed by {@link TileEntityPiston#getIndexForMergedShape(float, EnumDirection)} ++ */ ++ private static VoxelShape[] precomputePistonBaseWithMovingHeadShapes() { ++ float[] offsets = {0f, 0.5f, 1f}; ++ EnumDirection[] directions = EnumDirection.values(); ++ ++ VoxelShape[] mergedShapes = new VoxelShape[offsets.length * directions.length]; ++ ++ for (EnumDirection facing : directions) { ++ VoxelShape baseShape = Blocks.PISTON.getBlockData().set(BlockPiston.EXTENDED, true) ++ .set(BlockPiston.FACING, facing).getCollisionShape(null, null); ++ for (float offset : offsets) { ++ //this cache is only required for the merged piston head + base shape. ++ //this shape is only used when !this.extending ++ //here: isShort = this.extending != 1.0F - this.progress < 0.25F can be simplified to: ++ //isShort = f < 0.25F , because f = getAmountExtended(this.progress) can be simplified to f == 1.0F - this.progress ++ //therefore isShort is dependent on the offset: ++ boolean isShort = offset < 0.25f; ++ ++ VoxelShape headShape = (Blocks.PISTON_HEAD.getBlockData().set(BlockPistonExtension.FACING, facing)) ++ .set(BlockPistonExtension.SHORT, isShort).getCollisionShape(null, null); ++ ++ VoxelShape offsetHead = headShape.offset(facing.getAdjacentX() * offset, ++ facing.getAdjacentY() * offset, ++ facing.getAdjacentZ() * offset); ++ mergedShapes[getIndexForMergedShape(offset, facing)] = VoxelShapes.union(baseShape, offsetHead); ++ } ++ ++ } ++ ++ return mergedShapes; ++ } ++ ++ private static int getIndexForMergedShape(float offset, EnumDirection direction) { ++ if (offset != 0f && offset != 0.5f && offset != 1f) { ++ return -1; ++ } ++ //shape of offset 0 is still dependent on the direction, due to piston head and base being directional blocks ++ return (int) (2 * offset) + (3 * direction.get3DDataValue()); ++ } ++ // Yatopia End ++ + public TileEntityPiston() { + super(TileEntityTypes.PISTON); + } +@@ -350,13 +418,21 @@ public class TileEntityPiston extends TileEntity implements ITickable { + } else { + iblockdata = this.a; + } ++ float f = this.e(this.i); // Yatopia Start - Port Lithium ++ if (this.isExtending() || !this.isSource()) { ++ //here voxelShape2.isEmpty() is guaranteed, vanilla code would call union() which calls simplify() ++ VoxelShape blockShape = iblockdata.getCollisionShape(iblockaccess, blockposition); ++ ++ //we cache the simplified shapes, as the simplify() method costs a lot of CPU time and allocates several objects ++ VoxelShape offsetAndSimplified = getOffsetAndSimplified(blockShape, Math.abs(f), f < 0f ? this.getFacing().opposite() : this.getFacing()); ++ return offsetAndSimplified; ++ } else { ++ //retracting piston heads have to act like their base as well, as the base block is replaced with the moving block ++ //f >= 0f is guaranteed (assuming no other mod interferes) ++ int index = getIndexForMergedShape(f, this.getFacing()); ++ return PISTON_BASE_WITH_MOVING_HEAD_SHAPES[index]; ++ } // Yatopia End + +- float f = this.e(this.i); +- double d0 = (double) ((float) this.b.getAdjacentX() * f); +- double d1 = (double) ((float) this.b.getAdjacentY() * f); +- double d2 = (double) ((float) this.b.getAdjacentZ() * f); +- +- return VoxelShapes.a(voxelshape, iblockdata.getCollisionShape(iblockaccess, blockposition).a(d0, d1, d2)); + } + } + +diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +index 9567630593da7c77a0173b93c9a57ceb7887a59b..fbc5fab9946b6f9233f3014f0e69f2576b2138d7 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +@@ -13,9 +13,50 @@ import net.minecraft.util.MathHelper; + import net.minecraft.world.phys.AxisAlignedBB; + import net.minecraft.world.phys.MovingObjectPositionBlock; + import net.minecraft.world.phys.Vec3D; ++import me.jellysquid.mods.lithium.common.util.tuples.FinalObject; // Yatopia + + public abstract class VoxelShape { + ++ // Yatopia - Port Lithium ++ private FinalObject[] offsetAndSimplified; ++ ++ public void setShape(float offset, EnumDirection direction, VoxelShape offsetShape) { ++ if (offsetShape == null) { ++ throw new IllegalArgumentException("offsetShape must not be null!"); ++ } ++ int index = getIndexForOffsetSimplifiedShapes(offset, direction); ++ FinalObject[] offsetAndSimplified = this.offsetAndSimplified; ++ if (offsetAndSimplified == null) { ++ //noinspection unchecked ++ this.offsetAndSimplified = (offsetAndSimplified = new FinalObject[1 + (2 * 6)]); ++ } ++ //using FinalObject as it stores the value in a final field, which guarantees safe publication ++ offsetAndSimplified[index] = new FinalObject<>(offsetShape); ++ } ++ ++ public VoxelShape getOffsetSimplifiedShape(float offset, EnumDirection direction) { ++ FinalObject[] offsetAndSimplified = this.offsetAndSimplified; ++ if (offsetAndSimplified == null) { ++ return null; ++ } ++ int index = getIndexForOffsetSimplifiedShapes(offset, direction); ++ //usage of FinalObject guarantees that we are seeing a fully initialized VoxelShape here, even when it was created on a different thread ++ FinalObject wrappedShape = offsetAndSimplified[index]; ++ //noinspection FinalObjectAssignedToNull,FinalObjectGetWithoutIsPresent ++ return wrappedShape == null ? null : wrappedShape.getValue(); ++ } ++ ++ private static int getIndexForOffsetSimplifiedShapes(float offset, EnumDirection direction) { ++ if (offset != 0f && offset != 0.5f && offset != 1f) { ++ throw new IllegalArgumentException("offset must be one of {0f, 0.5f, 1f}"); ++ } ++ if (offset == 0f) { ++ return 0; //can treat offsetting by 0 in all directions the same ++ } ++ return (int) (2 * offset) + 2 * direction.get3DDataValue(); ++ } ++ // Yatopia End ++ + protected final VoxelShapeDiscrete a; public final VoxelShapeDiscrete getShape() { return this.a; } // Tuinity - OBFHELPER + @Nullable + private VoxelShape[] b; +diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java +index 98e787e6383a39de1708428137fd7f9e057ff153..049aa5f51e7517744b25f8c2c4b5e7a7de24a73e 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java +@@ -187,6 +187,7 @@ public final class VoxelShapes { + return (long) i * (long) (j / IntMath.gcd(i, j)); + } + ++ public static VoxelShape union(VoxelShape voxelshape, VoxelShape voxelshape1) { return a(voxelshape, voxelshape1); } // Yatopia - OBFHELPER + public static VoxelShape a(VoxelShape voxelshape, VoxelShape voxelshape1) { + return a(voxelshape, voxelshape1, OperatorBoolean.OR); + } diff --git a/patches/server/0067-lithium-entity.patch b/patches/server/0067-lithium-entity.patch new file mode 100644 index 00000000..45bcecc2 --- /dev/null +++ b/patches/server/0067-lithium-entity.patch @@ -0,0 +1,60 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JellySquid +Date: Sun, 24 Jan 2021 22:55:55 +0100 +Subject: [PATCH] lithium entity + +Co-authored-by: Hugo Planque + +diff --git a/src/main/java/net/minecraft/world/entity/EntityLiving.java b/src/main/java/net/minecraft/world/entity/EntityLiving.java +index bbe9eea55b18e1198916df29cbd922092b485eed..6a24932b17bb8d2e48334e61bfd5290c1c1e2be6 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityLiving.java ++++ b/src/main/java/net/minecraft/world/entity/EntityLiving.java +@@ -286,6 +286,10 @@ public abstract class EntityLiving extends Entity { + public NearbyEntityListenerMulti getListener() { + return this.tracker; + } ++ ++ private IBlockData lastStateAtFeet = null; ++ ++ private long lastPos = Long.MIN_VALUE; + // Yatopia end + protected void initAttributes() {} // Purpur + +@@ -363,6 +367,10 @@ public abstract class EntityLiving extends Entity { + + @Override + public void entityBaseTick() { ++ // Yatopia start - port lithium ++ this.lastStateAtFeet = null; ++ this.lastPos = Long.MIN_VALUE; ++ // Yatopia end + this.ar = this.as; + if (this.justCreated) { + this.getBedPosition().ifPresent(this::a); +@@ -1784,6 +1792,26 @@ public abstract class EntityLiving extends Entity { + + public IBlockData ds() { + return this.world.getBlockStateIfLoaded(this.getChunkCoordinates()); // Yatopia ++ /* ++ // Yatopia start - port lithium ++ int x = MathHelper.floor(this.locX()); ++ int y = MathHelper.floor(this.locY()); ++ int z = MathHelper.floor(this.locZ()); ++ ++ long pos = getChunkCoordinates().asLong(); ++ ++ if (this.lastPos == pos) { ++ return this.lastStateAtFeet; ++ } ++ ++ IBlockData state = this.world.getType(getChunkCoordinates()); ++ ++ this.lastPos = pos; ++ this.lastStateAtFeet = state; ++ ++ return state; ++ // Yatopia end ++ */ + } + + private boolean c(BlockPosition blockposition, IBlockData iblockdata) { diff --git a/patches/server/0068-lithium-gen.patch b/patches/server/0068-lithium-gen.patch new file mode 100644 index 00000000..48417c45 --- /dev/null +++ b/patches/server/0068-lithium-gen.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JellySquid +Date: Sun, 24 Jan 2021 23:42:56 +0100 +Subject: [PATCH] lithium gen + +Co-authored-by: Hugo Planque + +diff --git a/src/main/java/net/minecraft/world/level/newbiome/area/AreaLazy.java b/src/main/java/net/minecraft/world/level/newbiome/area/AreaLazy.java +index 69a021b84cd3f6e2a397a03cfbb562a94f72aace..9a897ad2b53e281cf165106d03737d2ec517eb29 100644 +--- a/src/main/java/net/minecraft/world/level/newbiome/area/AreaLazy.java ++++ b/src/main/java/net/minecraft/world/level/newbiome/area/AreaLazy.java +@@ -16,6 +16,7 @@ public final class AreaLazy implements Area { + this.a = areatransformer8; + } + ++ public int sample(int i, int j){ return a(i, j); } // Yatopia - OBFHELPER + @Override + public int a(int i, int j) { + long k = ChunkCoordIntPair.pair(i, j); +diff --git a/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayer.java b/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayer.java +index 8b61bce3d7587832ddb2c0e29e76f9c68ddf5d8b..acf74fccde2fc1fc3bd065f7d1bd2f5ba7f475e4 100644 +--- a/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayer.java ++++ b/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayer.java +@@ -15,13 +15,14 @@ public class GenLayer { + + private static final Logger LOGGER = LogManager.getLogger(); + private final AreaLazy b; +- ++ private final ThreadLocal tlSampler; // Yatopia - Port lithium + public GenLayer(AreaFactory areafactory) { + this.b = (AreaLazy) areafactory.make(); ++ this.tlSampler = ThreadLocal.withInitial(areafactory::make); // Yatopia - Port lithium + } + + public BiomeBase a(IRegistry iregistry, int i, int j) { +- int k = this.b.a(i, j); ++ int k = this.tlSampler.get().sample(i, j); // Yatopia - Port lithium + ResourceKey resourcekey = BiomeRegistry.a(k); + + if (resourcekey == null) { diff --git a/patches/server/0069-lithium-shape.patch b/patches/server/0069-lithium-shape.patch new file mode 100644 index 00000000..907302a4 --- /dev/null +++ b/patches/server/0069-lithium-shape.patch @@ -0,0 +1,358 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JellySquid +Date: Wed, 27 Jan 2021 21:05:05 +0100 +Subject: [PATCH] lithium shape + +Co-authored-by: Hugo Planque + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/block/LithiumEntityShapeContext.java b/src/main/java/me/jellysquid/mods/lithium/common/block/LithiumEntityShapeContext.java +new file mode 100644 +index 0000000000000000000000000000000000000000..85d55d144a38a16d0f148a29cd11985d7febe880 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/block/LithiumEntityShapeContext.java +@@ -0,0 +1,55 @@ ++package me.jellysquid.mods.lithium.common.block; ++ ++import net.minecraft.core.BlockPosition; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.EntityLiving; ++import net.minecraft.core.EnumDirection; ++import net.minecraft.world.level.material.Fluid; ++import net.minecraft.world.level.material.FluidTypeFlowing; ++import net.minecraft.world.item.Item; ++import net.minecraft.world.item.Items; ++import net.minecraft.world.phys.shapes.VoxelShape; ++import net.minecraft.world.phys.shapes.VoxelShapeCollision; ++ ++/** ++ * A replacement for EntityShapeContext that does not calculate the heldItem on construction. As most instances never ++ * use this field, a lazy evaluation is faster on average. The initialization of the heldItem field takes about 1% of ++ * the server thread CPU time in a fresh world with lots of animals (singleplayer 1.16.5, renderdistance 24). ++ * ++ * @author 2No2Name ++ */ ++public class LithiumEntityShapeContext implements VoxelShapeCollision { ++ private final Entity entity; ++ private final boolean descending; ++ private final double minY; ++ private Item heldItem; ++ ++ public LithiumEntityShapeContext(Entity entity) { ++ this.entity = entity; ++ this.descending = entity.isDescending(); ++ this.minY = entity.locY(); ++ } ++ ++ @Override ++ public boolean a(Item item) { ++ if (this.heldItem == null) { ++ this.heldItem = entity instanceof EntityLiving ? ((EntityLiving)entity).getItemInMainHand().getItem() : Items.AIR; ++ } ++ return this.heldItem == item; ++ } ++ ++ @Override ++ public boolean a(Fluid aboveState, FluidTypeFlowing fluid) { ++ return this.entity instanceof EntityLiving && ((EntityLiving) this.entity).a(fluid) && !aboveState.getType().a(fluid); ++ } ++ ++ @Override ++ public boolean b() { ++ return this.descending; ++ } ++ ++ @Override ++ public boolean a(VoxelShape shape, BlockPosition pos, boolean defaultValue) { ++ return this.minY > (double)pos.getY() + shape.getMax(EnumDirection.EnumAxis.Y) - 9.999999747378752E-6D; ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/shapes/pairs/LithiumDoublePairList.java b/src/main/java/me/jellysquid/mods/lithium/common/shapes/pairs/LithiumDoublePairList.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bfa06e714050260779fc8727b4b2cabb6f811398 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/shapes/pairs/LithiumDoublePairList.java +@@ -0,0 +1,117 @@ ++package me.jellysquid.mods.lithium.common.shapes.pairs; ++ ++import it.unimi.dsi.fastutil.doubles.DoubleArrayList; ++import it.unimi.dsi.fastutil.doubles.DoubleList; ++import net.minecraft.world.phys.shapes.VoxelShapeMerger; ++ ++/** ++ * Optimized variant of {@link net.minecraft.util.shape.SimplePairList}. This implementation works directly against ++ * flat arrays and tries to organize code in a manner that hits the JIT's happy path. In my testing, this is about ++ * ~50% faster than the vanilla implementation. ++ */ ++public final class LithiumDoublePairList implements VoxelShapeMerger { ++ private final double[] merged; ++ private final int[] indicesFirst; ++ private final int[] indicesSecond; ++ ++ private final DoubleArrayList pairs; ++ ++ public LithiumDoublePairList(DoubleList aPoints, DoubleList bPoints, boolean flag1, boolean flag2) { ++ int size = aPoints.size() + bPoints.size(); ++ ++ this.merged = new double[size]; ++ this.indicesFirst = new int[size]; ++ this.indicesSecond = new int[size]; ++ ++ this.pairs = DoubleArrayList.wrap(this.merged); ++ ++ this.merge(getArray(aPoints), getArray(bPoints), aPoints.size(), bPoints.size(), flag1, flag2); ++ } ++ ++ private void merge(double[] aPoints, double[] bPoints, int aSize, int bSize, boolean flag1, boolean flag2) { ++ int aIdx = 0; ++ int bIdx = 0; ++ ++ double prev = 0.0D; ++ ++ int a1 = 0, a2 = 0; ++ ++ while (true) { ++ boolean aWithinBounds = aIdx < aSize; ++ boolean bWithinBounds = bIdx < bSize; ++ ++ if (!aWithinBounds && !bWithinBounds) { ++ break; ++ } ++ ++ boolean flip = aWithinBounds && (!bWithinBounds || aPoints[aIdx] < bPoints[bIdx] + 1.0E-7D); ++ ++ double value; ++ ++ if (flip) { ++ value = aPoints[aIdx++]; ++ } else { ++ value = bPoints[bIdx++]; ++ } ++ ++ if ((aIdx == 0 || !aWithinBounds) && !flip && !flag2) { ++ continue; ++ } ++ ++ if ((bIdx == 0 || !bWithinBounds) && flip && !flag1) { ++ continue; ++ } ++ ++ if (a2 == 0 || prev < value - 1.0E-7D) { ++ this.indicesFirst[a1] = aIdx - 1; ++ this.indicesSecond[a1] = bIdx - 1; ++ this.merged[a2] = value; ++ ++ a1++; ++ a2++; ++ prev = value; ++ } else if (a2 > 0) { ++ this.indicesFirst[a1 - 1] = aIdx - 1; ++ this.indicesSecond[a1 - 1] = bIdx - 1; ++ } ++ } ++ ++ if (a2 == 0) { ++ this.merged[a2++] = Math.min(aPoints[aSize - 1], bPoints[bSize - 1]); ++ } ++ ++ this.pairs.size(a2); ++ } ++ ++ @Override ++ public boolean a(VoxelShapeMerger.a predicate) { ++ int l = this.pairs.size() - 1; ++ ++ for (int i = 0; i < l; i++) { ++ if (!predicate.merge(this.indicesFirst[i], this.indicesSecond[i], i)) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ @Override ++ public DoubleList a() { ++ return this.pairs; ++ } ++ ++ private static double[] getArray(DoubleList list) { ++ if (list instanceof DoubleArrayList) { ++ return ((DoubleArrayList) list).elements(); ++ } ++ ++ double[] points = new double[list.size()]; ++ ++ for (int i = 0; i < points.length; i++) { ++ points[i] = list.getDouble(i); ++ } ++ ++ return points; ++ } ++} +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/Object2BooleanCacheTable.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/Object2BooleanCacheTable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e210e0fa39b74805429832c3d232fadb33975414 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/Object2BooleanCacheTable.java +@@ -0,0 +1,61 @@ ++package me.jellysquid.mods.lithium.common.util.collections; ++ ++import it.unimi.dsi.fastutil.HashCommon; ++import net.minecraft.util.MathHelper; ++ ++import java.util.function.Predicate; ++ ++/** ++ * A lossy hashtable implementation that stores a mapping between an object and a boolean. ++ *

++ * Any hash collisions will result in an overwrite: this is safe because the correct value can always be recomputed, ++ * given that the given operator is deterministic. ++ *

++ * This implementation is safe to use from multiple threads ++ */ ++public final class Object2BooleanCacheTable { ++ private final int capacity; ++ private final int mask; ++ ++ private final Node[] nodes; ++ ++ private final Predicate operator; ++ ++ @SuppressWarnings("unchecked") ++ public Object2BooleanCacheTable(int capacity, Predicate operator) { ++ this.capacity = MathHelper.smallestEncompassingPowerOfTwo(capacity); ++ this.mask = this.capacity - 1; ++ ++ this.nodes = (Node[]) new Node[this.capacity]; ++ ++ this.operator = operator; ++ } ++ ++ private static int hash(T key) { ++ return HashCommon.mix(key.hashCode()); ++ } ++ ++ public boolean get(T key) { ++ int idx = hash(key) & this.mask; ++ ++ Node node = this.nodes[idx]; ++ if (node != null && key.equals(node.key)) { ++ return node.value; ++ } ++ ++ boolean test = this.operator.test(key); ++ this.nodes[idx] = new Node<>(key, test); ++ ++ return test; ++ } ++ ++ static class Node { ++ final T key; ++ final boolean value; ++ ++ Node(T key, boolean value) { ++ this.key = key; ++ this.value = value; ++ } ++ } ++} +diff --git a/src/main/java/net/minecraft/util/MathHelper.java b/src/main/java/net/minecraft/util/MathHelper.java +index b95115aca72ba0cf6451096ddbd8b50a8f3bb5c6..0afb8c643cb3e5938e12183c6132797d6ed645bb 100644 +--- a/src/main/java/net/minecraft/util/MathHelper.java ++++ b/src/main/java/net/minecraft/util/MathHelper.java +@@ -198,6 +198,7 @@ public class MathHelper { + return c(f, f + f3, f2); + } + ++ public static int smallestEncompassingPowerOfTwo(int i){ return c(i); } // Yatopia - OBFHELPER + public static int c(int i) { + int j = i - 1; + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 7befe4263a2d046922438e1a9853f2d8290ee230..ab5fb00885d22126639e7459ef71c56be4b5eb9e 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2647,6 +2647,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne + return this.isSneaking(); + } + ++ public boolean isDescending() { return by(); } // Yatopia - OBFHELPER + public boolean by() { + return this.isSneaking(); + } +diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java +index 3319c419a9eaf91224396c8b19eb057abd8094e6..9dee9ff25ba12f4de6d9080176c371627215b029 100644 +--- a/src/main/java/net/minecraft/world/level/block/Block.java ++++ b/src/main/java/net/minecraft/world/level/block/Block.java +@@ -56,6 +56,7 @@ import net.minecraft.world.phys.shapes.VoxelShapes; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + import me.jellysquid.mods.lithium.common.ai.pathing.PathNodeDefaults; ++import me.jellysquid.mods.lithium.common.util.collections.Object2BooleanCacheTable; + + public class Block extends BlockBase implements IMaterial { + +@@ -229,8 +230,14 @@ public class Block extends BlockBase implements IMaterial { + return a(voxelshape1); + } + ++ // Yatopia start - Port lithium ++ private static final Object2BooleanCacheTable FULL_CUBE_CACHE = new Object2BooleanCacheTable<>( ++ 512, ++ shape -> !VoxelShapes.applyOperation(VoxelShapes.fullCube(), shape, OperatorBoolean.NOT_SAME) ++ ); ++ + public static boolean a(VoxelShape voxelshape) { +- return (Boolean) Block.a.getUnchecked(voxelshape); ++ return FULL_CUBE_CACHE.get(voxelshape); // Yatopia end + } + + public boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { +diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +index fbc5fab9946b6f9233f3014f0e69f2576b2138d7..012892063405616c8ae35b6e06020008dfd147df 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java +@@ -71,6 +71,7 @@ public abstract class VoxelShape { + return i >= this.a.c(enumdirection_enumaxis) ? Double.POSITIVE_INFINITY : this.a(enumdirection_enumaxis, i); + } + ++ public double getMax(EnumDirection.EnumAxis enumdirection_enumaxis) { return c(enumdirection_enumaxis); } // Yatopia - OBFHELPER + public double c(EnumDirection.EnumAxis enumdirection_enumaxis) { + int i = this.a.b(enumdirection_enumaxis); + +diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeMerger.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeMerger.java +index d2a46ce0c5c980d34dc2f4b716a174a81a68f1d0..0c594866cb291abb2e90b149d52445a628331879 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeMerger.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeMerger.java +@@ -2,7 +2,7 @@ package net.minecraft.world.phys.shapes; + + import it.unimi.dsi.fastutil.doubles.DoubleList; + +-interface VoxelShapeMerger { ++public interface VoxelShapeMerger { // Yatopia - make Public + + DoubleList a(); + +diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java +index 049aa5f51e7517744b25f8c2c4b5e7a7de24a73e..173197e9dc6acb31aa41ce645224fd9a2733f27c 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java +@@ -5,6 +5,8 @@ import com.google.common.math.DoubleMath; + import com.google.common.math.IntMath; + import it.unimi.dsi.fastutil.doubles.DoubleArrayList; + import it.unimi.dsi.fastutil.doubles.DoubleList; ++import me.jellysquid.mods.lithium.common.shapes.pairs.LithiumDoublePairList; // Yatopia ++ + import java.util.Arrays; + import java.util.Iterator; + import java.util.Objects; +@@ -476,7 +478,7 @@ public final class VoxelShapes { + // doublelist is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause + // This is actually the most common path, so jump to it straight away + if (doublelist.getDouble(0) == Double.NEGATIVE_INFINITY && doublelist.getDouble(doublelist.size() - 1) == Double.POSITIVE_INFINITY) { +- return new VoxelShapeMergerList(doublelist, doublelist1, flag, flag1); ++ return new LithiumDoublePairList(doublelist, doublelist1, flag, flag1); // Yatopia - Port lithium + } + // Split out rest to hopefully inline the above + return lessCommonMerge(i, doublelist, doublelist1, flag, flag1); diff --git a/patches/server/0070-lithium-skip-ticking-block-entities-that-are-doing-n.patch b/patches/server/0070-lithium-skip-ticking-block-entities-that-are-doing-n.patch new file mode 100644 index 00000000..38e3d9f1 --- /dev/null +++ b/patches/server/0070-lithium-skip-ticking-block-entities-that-are-doing-n.patch @@ -0,0 +1,1218 @@ +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 db2b1863b7f46be53839fb3e86870745fb7567fd..8cecf61169d28b7ddb8e1af180ac8bc0e5fef324 100644 +--- a/src/main/java/net/minecraft/world/level/World.java ++++ b/src/main/java/net/minecraft/world/level/World.java +@@ -98,8 +98,11 @@ import org.bukkit.event.block.BlockPhysicsEvent; + + import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia + import me.jellysquid.mods.lithium.common.entity.tracker.EntityTrackerEngine; // 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); +@@ -108,9 +111,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; +@@ -371,6 +382,8 @@ public abstract class World implements GeneratorAccess, AutoCloseable, NonBlocki + this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime); + this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime); + this.tracker = new EntityTrackerEngine(); // Yatopia - Port lithium ++ this.tileEntityListTick$lithium = new MaskedTickingBlockEntityList<>(this.tileEntityListTick, blockEntity -> ((SleepingBlockEntity) blockEntity).canTickOnSide(false)); // Yatopia ++ this.tileEntityListTick = tileEntityListTick$lithium; // Yatopia + } + + // Yatopia start - Port lithium +@@ -1041,6 +1054,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(); + +@@ -1063,11 +1085,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"); +@@ -1105,8 +1135,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 +@@ -1119,8 +1148,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 8a049d3de8937a6c8afe178ccd134e2511fb3baf..9b447a9416613f0ccfeb0f81d5b099fcd0417df7 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 +@@ -27,8 +27,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 d97acd72c30e1051f3e14c3c23b1a6b6e6fa42f0..b11c260908791d7fca711954ae0b715a9bd0634f 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,25 @@ public class TileEntityBeehive extends TileEntity implements ITickable { + @Nullable + public BlockPosition flowerPos = null; + public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold ++ 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 +321,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 +347,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 8a1425f565204fedd1007862b2af9db4c9217461..6083bec6879a625168bd55944d5c21e8151b8189 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) { From f43855078b70bd25c958ca4050a0ec58b5eb2060 Mon Sep 17 00:00:00 2001 From: foss-mc <69294560+foss-mc@users.noreply.github.com> Date: Mon, 19 Apr 2021 15:42:41 +0000 Subject: [PATCH 4/4] fix typo in Upstream.kt (#465) * fix typo in Upstream.kt * lowercase api --- buildSrc/src/main/kotlin/Upstream.kt | 4 ++-- upstreamConfig/0005-Empirecraft.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/Upstream.kt b/buildSrc/src/main/kotlin/Upstream.kt index e5857b13..532cf499 100644 --- a/buildSrc/src/main/kotlin/Upstream.kt +++ b/buildSrc/src/main/kotlin/Upstream.kt @@ -15,7 +15,7 @@ open class Upstream(in_name: String, in_useBlackList: Boolean, in_list: String, var serverList = list.stream().filter { patch -> patch.startsWith("server/") } ?.sorted()?.map { patch -> patch.substring(7, patch.length) }?.collect(Collectors.toList()) - var apiList = list.stream().filter { patch -> patch.startsWith("API/") } + var apiList = list.stream().filter { patch -> patch.startsWith("api/") } ?.sorted()?.map { patch -> patch.substring(4, patch.length) }?.collect(Collectors.toList()) @@ -85,4 +85,4 @@ open class Upstream(in_name: String, in_useBlackList: Boolean, in_list: String, } return filesList } -} \ No newline at end of file +} diff --git a/upstreamConfig/0005-Empirecraft.properties b/upstreamConfig/0005-Empirecraft.properties index 44572f58..1ed11fcb 100644 --- a/upstreamConfig/0005-Empirecraft.properties +++ b/upstreamConfig/0005-Empirecraft.properties @@ -1,4 +1,4 @@ name=Empirecraft useBlackList=false -list=API/Add-ChatColor.getById.patch,server/Don-t-trigger-Lootable-Refresh-for-non-player-intera.patch,server/Fix-Bukkit.createInventory-with-type-LECTERN.patch,server/dont-load-chunks-for-physics.patch +list=api/Add-ChatColor.getById.patch,server/Don-t-trigger-Lootable-Refresh-for-non-player-intera.patch,server/Fix-Bukkit.createInventory-with-type-LECTERN.patch,server/dont-load-chunks-for-physics.patch branch=origin/master