diff --git a/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch index bd3c161a79..ba060d521d 100644 --- a/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch +++ b/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Add Debug Entities option to debug dupe uuid issues Add -Ddebug.entities=true to your JVM flags to gain more information diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 58f7489ebd..d974cb5c61 100644 +index 064bd4133a..7d8f723968 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke diff --git a/Spigot-Server-Patches/Add-World-Util-Methods.patch b/Spigot-Server-Patches/Add-World-Util-Methods.patch index 62f3dd99de..3c38a625aa 100644 --- a/Spigot-Server-Patches/Add-World-Util-Methods.patch +++ b/Spigot-Server-Patches/Add-World-Util-Methods.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Add World Util Methods Methods that can be used for other patches to help improve logic. diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 68f845986..dcd122d8b 100644 +index 68f8459861..dcd122d8b5 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -0,0 +0,0 @@ public class Chunk implements IChunkAccess { @@ -17,8 +17,44 @@ index 68f845986..dcd122d8b 100644 public int a(BlockPosition blockposition, int i) { return this.a(blockposition, i, this.world.o().g()); } +diff --git a/src/main/java/net/minecraft/server/IWorldReader.java b/src/main/java/net/minecraft/server/IWorldReader.java +index ac85986a1a..06c5a54254 100644 +--- a/src/main/java/net/minecraft/server/IWorldReader.java ++++ b/src/main/java/net/minecraft/server/IWorldReader.java +@@ -0,0 +0,0 @@ public interface IWorldReader extends IBlockAccess { + } + + int getLightLevel(BlockPosition var1, int var2); ++ // Paper start ++ default @Nullable ++ IBlockData getTypeIfLoaded(BlockPosition var1) { ++ return isLoaded(var1) ? getType(var1) : null; ++ } ++ ++ default @Nullable ++ Block getBlockIfLoaded(BlockPosition var1) { ++ return isLoaded(var1) ? getType(var1).getBlock() : null; ++ } ++ ++ default @Nullable ++ Material getMaterialIfLoaded(BlockPosition var1) { ++ return isLoaded(var1) ? getType(var1).getMaterial() : null; ++ } ++ // Paper end + + boolean isChunkLoaded(int var1, int var2, boolean var3); + +@@ -0,0 +0,0 @@ public interface IWorldReader extends IBlockAccess { + WorldBorder worldborder = this.getWorldBorder(); + boolean flag1 = worldborder.b() < (double)i && (double)j < worldborder.d() && worldborder.c() < (double)i1 && (double)j1 < worldborder.e(); + VoxelShapeBitSet voxelshapebitset = new VoxelShapeBitSet(j - i, l - k, j1 - i1); +- Predicate predicate = (voxelshape3) -> { ++ Predicate predicate = (voxelshape3) -> { // Paper - decompile fix + return !voxelshape3.b() && VoxelShapes.c(voxelshape, voxelshape3, OperatorBoolean.AND); + }; + Stream stream = StreamSupport.stream(BlockPosition.MutableBlockPosition.b(i, k, i1, j - 1, l - 1, j1 - 1).spliterator(), false).map((blockposition$mutableblockposition) -> { diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 561bcd07b..94872593b 100644 +index 561bcd07b3..94872593b1 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc diff --git a/Spigot-Server-Patches/Chunk-Save-Stats-Debug-Option.patch b/Spigot-Server-Patches/Chunk-Save-Stats-Debug-Option.patch index 78dc6e22cd..7e915bea4b 100644 --- a/Spigot-Server-Patches/Chunk-Save-Stats-Debug-Option.patch +++ b/Spigot-Server-Patches/Chunk-Save-Stats-Debug-Option.patch @@ -8,7 +8,7 @@ Adds a command line flag to enable stats on how chunk saves are processing. Stats on current queue, how many was processed and how many were queued. diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 87744dcbfc..355186c111 100644 +index 8acbd7bbff..edb4f2bb65 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { @@ -54,7 +54,7 @@ index 87744dcbfc..355186c111 100644 return false; } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 2415404d69..f099b91f7a 100644 +index fc8f7574cf..94ed728c0e 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { @@ -73,13 +73,13 @@ index 2415404d69..f099b91f7a 100644 // CraftBukkit start - Add async variant, provide compatibility @Nullable @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier nbttagcompound) { // Spigot + this.saveMap.put(chunkcoordintpair.asLong(), nbttagcompound); // Paper + queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements ++ queuedSaves++; // Paper + FileIOThread.a().a(this); } - protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier nbttagcompound) { // Spigot -+ queuedSaves++; // Paper - synchronized (this.b) { // Paper - synchronize while modifying the map - queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements - this.b.put(chunkcoordintpair, nbttagcompound); @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { // Paper end ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements diff --git a/Spigot-Server-Patches/Chunk-save-queue-improvements.patch b/Spigot-Server-Patches/Chunk-save-queue-improvements.patch index eb84bff62f..edc6c315b8 100644 --- a/Spigot-Server-Patches/Chunk-save-queue-improvements.patch +++ b/Spigot-Server-Patches/Chunk-save-queue-improvements.patch @@ -40,8 +40,20 @@ index 0d68ffd75a..fd00c320ce 100644 + if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues"); + } } +diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java +index d9608121b6..d7a6700936 100644 +--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java ++++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java +@@ -0,0 +0,0 @@ public class ChunkCoordIntPair { + this.z = (int)(i >> 32); + } + ++ public long asLong() { return a(); } // Paper + public long a() { + return a(this.x, this.z); + } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 2e9bd0949a..1dbcedbf94 100644 +index f969c036f3..e831ea1429 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +0,0 @@ import java.util.function.Consumer; @@ -77,16 +89,26 @@ index 2e9bd0949a..1dbcedbf94 100644 + // Paper end + private static final Logger a = LogManager.getLogger(); - private final Map> b = java.util.Collections.synchronizedMap(Maps.newHashMap()); // CraftBukkit // Spigot +- private final Map> b = Maps.newHashMap(); ++ private final it.unimi.dsi.fastutil.longs.Long2ObjectMap> saveMap = it.unimi.dsi.fastutil.longs.Long2ObjectMaps.synchronize(new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Paper private final File c; + private final DataFixer d; + private PersistentStructureLegacy e; +@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + + @Nullable + private NBTTagCompound a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection, int i, int j, @Nullable GeneratorAccess generatoraccess) throws IOException { +- NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(new ChunkCoordIntPair(i, j))); // Spigot ++ NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.saveMap.get(ChunkCoordIntPair.asLong(i, j))); // Spigot // Paper + + if (nbttagcompound != null) { + return nbttagcompound; @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - } }; } -- + - this.a(chunkcoordintpair, SupplierUtils.createUnivaluedSupplier(completion, unloaded && this.b.size() < SAVE_QUEUE_TARGET_SIZE)); + this.a(chunkcoordintpair, SupplierUtils.createUnivaluedSupplier(completion, unloaded)); // Paper - Remove save queue target size -+ // Paper end // Spigot end } catch (Exception exception) { ChunkRegionLoader.a.error("Failed to save chunk", exception); @@ -95,10 +117,8 @@ index 2e9bd0949a..1dbcedbf94 100644 protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier nbttagcompound) { // Spigot - this.b.put(chunkcoordintpair, nbttagcompound); -+ synchronized (this.b) { // Paper - synchronize while modifying the map -+ queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements -+ this.b.put(chunkcoordintpair, nbttagcompound); -+ } ++ this.saveMap.put(chunkcoordintpair.asLong(), nbttagcompound); // Paper ++ queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements FileIOThread.a().a(this); } @@ -106,7 +126,6 @@ index 2e9bd0949a..1dbcedbf94 100644 } private boolean processSaveQueueEntry(boolean logCompletion) { -- synchronized (this.b) { // CraftBukkit - Iterator iterator = this.b.entrySet().iterator(); - if (!iterator.hasNext()) { + // Paper start - Chunk queue improvements @@ -140,25 +159,17 @@ index 2e9bd0949a..1dbcedbf94 100644 RegionFileCache.write(this.c, chunkcoordintpair.x, chunkcoordintpair.z, SupplierUtils.getIfExists(nbttagcompound)); // Spigot + // Paper start remove from map only if this was the latest version of the chunk -+ synchronized (this.b) { ++ synchronized (this.saveMap) { ++ long k = chunkcoordintpair.asLong(); + // This will not equal if a newer version is still pending - wait until newest is saved to remove -+ if (this.b.get(chunkcoordintpair) == chunk.compoundSupplier) { -+ this.b.remove(chunkcoordintpair); ++ if (this.saveMap.get(k) == chunk.compoundSupplier) { ++ this.saveMap.remove(k); + } + } + // Paper end /* NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream); dataoutputstream.close(); -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - return true; - } - } -- } // CraftBukkit -+ // } // CraftBukkit // Paper - } - - private ChunkStatus.Type a(@Nullable NBTTagCompound nbttagcompound) { diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java index a3aba244af..97917551a4 100644 --- a/src/main/java/net/minecraft/server/FileIOThread.java diff --git a/Spigot-Server-Patches/Fix-Double-World-Add-issues.patch b/Spigot-Server-Patches/Fix-Double-World-Add-issues.patch index 8bb577624c..9be854c54e 100644 --- a/Spigot-Server-Patches/Fix-Double-World-Add-issues.patch +++ b/Spigot-Server-Patches/Fix-Double-World-Add-issues.patch @@ -8,7 +8,7 @@ Vanilla will double add Spider Jockeys to the world, so ignore already added. Also add debug if something else tries to, and abort before world gets bad state diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 1dbcedbf94..8e14f8c56c 100644 +index e831ea1429..d40d9d1173 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { diff --git a/Spigot-Server-Patches/Optimize-Chunk-Access.patch b/Spigot-Server-Patches/Optimize-Chunk-Access.patch index 2aed74e42b..3c652c123c 100644 --- a/Spigot-Server-Patches/Optimize-Chunk-Access.patch +++ b/Spigot-Server-Patches/Optimize-Chunk-Access.patch @@ -44,7 +44,7 @@ index fbebd4591c..b941676829 100644 public Chunk a(Object object) { return this.a(((Long) object).longValue()); diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 6b041f06e4..1a1daf36b7 100644 +index d73034f329..b1e6901090 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { @@ -77,12 +77,12 @@ index 6b041f06e4..1a1daf36b7 100644 this.asyncTaskHandler.postToMainThread(chunk::addEntities); @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - this.saveChunk(chunk, true); // Spigot + this.saveChunk(chunk, true); // Spigot + } + this.chunks.remove(chunk.chunkKey); +- this.lastChunk = null; ++ // this.lastChunk = null; // Paper } - this.chunks.remove(chunk.chunkKey); -- this.lastChunk = null; -+ //this.lastChunk = null; // Paper return true; } - // CraftBukkit end -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Prevent-Saving-Bad-entities-to-chunks.patch b/Spigot-Server-Patches/Prevent-Saving-Bad-entities-to-chunks.patch index 2cacd917f5..fc2bf54453 100644 --- a/Spigot-Server-Patches/Prevent-Saving-Bad-entities-to-chunks.patch +++ b/Spigot-Server-Patches/Prevent-Saving-Bad-entities-to-chunks.patch @@ -18,7 +18,7 @@ an invalid entity. This should reduce log occurrences of dupe uuid messages. diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index ec94b9fe57..e6ede2cc25 100644 +index 4f9be4b86d..f22532f071 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { diff --git a/Spigot-Server-Patches/Provide-option-to-use-a-versioned-world-folder-for-t.patch b/Spigot-Server-Patches/Provide-option-to-use-a-versioned-world-folder-for-t.patch index a2f84f95f5..dc13254101 100644 --- a/Spigot-Server-Patches/Provide-option-to-use-a-versioned-world-folder-for-t.patch +++ b/Spigot-Server-Patches/Provide-option-to-use-a-versioned-world-folder-for-t.patch @@ -59,7 +59,7 @@ index bcdf4f91d8..c457d07110 100644 + } } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index e6ede2cc25..93bc613958 100644 +index f22532f071..7db075b731 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { @@ -71,29 +71,40 @@ index e6ede2cc25..93bc613958 100644 + private final File actualWorld; + private final boolean useAltWorld; + -+ private synchronized void copyIfNeeded(int x, int z) { ++ private void copyIfNeeded(int x, int z) { + if (!useAltWorld) { + return; + } -+ if (RegionFileCache.hasRegionFile(this.actualWorld, x, z)) { -+ return; -+ } -+ File actual = RegionFileCache.getRegionFileName(this.actualWorld, x, z); -+ File template = RegionFileCache.getRegionFileName(this.templateWorld, x, z); -+ if (!actual.exists() && template.exists()) { -+ try { -+ //a.info("Copying" + template + " to " + actual); -+ java.nio.file.Files.copy(template.toPath(), actual.toPath(), java.nio.file.StandardCopyOption.COPY_ATTRIBUTES); -+ } catch (IOException e1) { -+ LogManager.getLogger().error("Error copying " + template + " to " + actual, e1); -+ MinecraftServer.getServer().safeShutdown(); -+ org.spigotmc.SneakyThrow.sneaky(e1); ++ synchronized (RegionFileCache.class) { ++ if (RegionFileCache.hasRegionFile(this.actualWorld, x, z)) { ++ return; ++ } ++ File actual = RegionFileCache.getRegionFileName(this.actualWorld, x, z); ++ File template = RegionFileCache.getRegionFileName(this.templateWorld, x, z); ++ if (!actual.exists() && template.exists()) { ++ try { ++ //a.info("Copying" + template + " to " + actual); ++ java.nio.file.Files.copy(template.toPath(), actual.toPath(), java.nio.file.StandardCopyOption.COPY_ATTRIBUTES); ++ } catch (IOException e1) { ++ LogManager.getLogger().error("Error copying " + template + " to " + actual, e1); ++ MinecraftServer.getServer().safeShutdown(); ++ org.spigotmc.SneakyThrow.sneaky(e1); ++ } + } + } - + } ++ ++ public boolean chunkExists(int x, int z) { ++ if (this.saveMap.containsKey(ChunkCoordIntPair.asLong(x, z))) { ++ return true; ++ } ++ copyIfNeeded(x, z); ++ return RegionFileCache.chunkExists(this.actualWorld, x, z); ++ } ++ // Paper end + public ChunkRegionLoader(File file, DataFixer datafixer) { -+ // Paper ++ // Paper start + this.actualWorld = file; + if (com.destroystokyo.paper.PaperConfig.useVersionedWorld) { + this.useAltWorld = true; @@ -129,25 +140,9 @@ index e6ede2cc25..93bc613958 100644 @Nullable private NBTTagCompound a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection, int i, int j, @Nullable GeneratorAccess generatoraccess) throws IOException { + copyIfNeeded(i, j); // Paper - NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(new ChunkCoordIntPair(i, j))); // Spigot + NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.saveMap.get(ChunkCoordIntPair.asLong(i, j))); // Spigot // Paper if (nbttagcompound != null) { -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - } - } - -+ public boolean chunkExists(int x, int z) { -+ ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(x, z); -+ if (this.b.containsKey(chunkcoordintpair)) { -+ return true; -+ } -+ copyIfNeeded(x, z); -+ return RegionFileCache.chunkExists(this.actualWorld, x, z); -+ } -+ - @Nullable - protected Object[] a(GeneratorAccess generatoraccess, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[] - if (nbttagcompound.hasKeyOfType("Level", 10) && nbttagcompound.getCompound("Level").hasKeyOfType("Status", 8)) { diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java index 15666325ea..3501b87f75 100644 --- a/src/main/java/net/minecraft/server/RegionFileCache.java diff --git a/Spigot-Server-Patches/Timings-v2.patch b/Spigot-Server-Patches/Timings-v2.patch index 17698a7eae..9dc9ebf723 100644 --- a/Spigot-Server-Patches/Timings-v2.patch +++ b/Spigot-Server-Patches/Timings-v2.patch @@ -348,7 +348,7 @@ index e3d1761b49..fbebd4591c 100644 // CraftBukkit end diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 9e805c5d22..0034956af9 100644 +index 3045d6d063..68212aa26e 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { @@ -389,7 +389,7 @@ index 9e805c5d22..0034956af9 100644 this.chunkLoader.saveChunk(this.world, ichunkaccess, unloaded); // Spigot } catch (IOException ioexception) { diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 664b167bb2..2e9bd0949a 100644 +index df07b2b889..f969c036f3 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -0,0 +0,0 @@ diff --git a/work/CraftBukkit b/work/CraftBukkit index 022b8c350a..7033f180e0 160000 --- a/work/CraftBukkit +++ b/work/CraftBukkit @@ -1 +1 @@ -Subproject commit 022b8c350a223f092e4fdbdce8ffb682f412fb29 +Subproject commit 7033f180e0f1f300f6e85beda70cb396200a219a diff --git a/work/Spigot b/work/Spigot index 145a37ae8c..500ff5d408 160000 --- a/work/Spigot +++ b/work/Spigot @@ -1 +1 @@ -Subproject commit 145a37ae8c1032ac0dd0ea0a9f93c235ab2a6423 +Subproject commit 500ff5d408d9ce45b6a0819777fc7ad4779f32e4