From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: wangxyper Date: Sun, 15 Jan 2023 09:57:50 +0800 Subject: [PATCH] Hearse: MC Code changes Original license: MIT Original project: https://github.com/Era4FunMC/Hearse diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java index 24c677e80af652952263253409c050641e72e3b5..c6f9fb3efb92de0879eab6389fabd531bb4cfcb2 100644 --- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java @@ -1,7 +1,8 @@ package net.minecraft.network.protocol.game; -import it.unimi.dsi.fastutil.shorts.ShortIterator; import it.unimi.dsi.fastutil.shorts.ShortSet; +import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -22,14 +23,16 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet copy = new ArrayList<>(positions); + this.positions = new short[copy.size()]; + this.states = new BlockState[copy.size()]; + for (int i = 0; i < copy.size(); i++) { + this.positions[i] = copy.get(i); + } - this.positions = new short[i]; - this.states = new BlockState[i]; - int j = 0; - for (ShortIterator shortiterator = positions.iterator(); shortiterator.hasNext(); ++j) { - short short0 = (Short) shortiterator.next(); + for (int j = 0;j < this.positions.length;j++) { + short short0 = this.positions[j]; this.positions[j] = short0; this.states[j] = (section != null) ? section.getBlockState(SectionPos.sectionRelativeX(short0), SectionPos.sectionRelativeY(short0), SectionPos.sectionRelativeZ(short0)) : net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(); // CraftBukkit - SPIGOT-6076, Mojang bug when empty chunk section notified diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 3b7e4b724e86518ea57f5ed5ef0b8b3741d10f6f..42599340856aa5ffcfae2281684174ca4dee289a 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1,10 +1,11 @@ package net.minecraft.server; +import co.earthme.hearse.Hearse; +import co.earthme.hearse.HearseConfig; +import co.earthme.hearse.server.ServerEntityTickHook; +import co.earthme.hearse.server.ServerLevelTickHook; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; -import co.aikar.timings.Timings; -import com.destroystokyo.paper.event.server.PaperServerListPingEvent; -import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -85,7 +86,6 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.server.level.TicketType; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListenerFactory; import net.minecraft.server.network.ServerConnectionListener; @@ -109,17 +109,14 @@ import net.minecraft.util.NativeModuleLister; import net.minecraft.util.ProgressListener; import net.minecraft.util.RandomSource; import net.minecraft.util.SignatureValidator; -import net.minecraft.util.Unit; import net.minecraft.util.datafix.DataFixers; import net.minecraft.util.profiling.EmptyProfileResults; import net.minecraft.util.profiling.ProfileResults; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.profiling.ResultField; -import net.minecraft.util.profiling.SingleTickProfiler; import net.minecraft.util.profiling.jfr.JvmProfiler; import net.minecraft.util.profiling.jfr.callback.ProfiledDuration; import net.minecraft.util.profiling.metrics.profiling.ActiveMetricsRecorder; -import net.minecraft.util.profiling.metrics.profiling.InactiveMetricsRecorder; import net.minecraft.util.profiling.metrics.profiling.MetricsRecorder; import net.minecraft.util.profiling.metrics.profiling.ServerMetricsSamplersProvider; import net.minecraft.util.profiling.metrics.storage.MetricsPersister; @@ -183,12 +180,6 @@ import net.minecraft.world.level.levelgen.PatrolSpawner; import net.minecraft.world.level.levelgen.PhantomSpawner; import net.minecraft.world.level.levelgen.WorldDimensions; import net.minecraft.world.level.levelgen.presets.WorldPresets; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.Main; -import org.bukkit.craftbukkit.util.CraftChatMessage; -import org.bukkit.craftbukkit.util.LazyPlayerSet; -import org.bukkit.event.player.AsyncPlayerChatPreviewEvent; import org.bukkit.event.server.ServerLoadEvent; // CraftBukkit end @@ -233,7 +224,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop registries; - private Map, ServerLevel> levels; + public Map, ServerLevel> levels; private PlayerList playerList; private volatile boolean running; private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart @@ -922,10 +913,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { this.lastServerStatus = i; this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); @@ -1522,11 +1516,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 0; // Paper - worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper + for (ServerLevel worldserver : this.getAllLevels()) { + worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper + worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper /*this.profiler.push(() -> { // Purpur @@ -1541,35 +1533,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296 if (this.changedBlocksPerSection[i] == null) { this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration - this.changedBlocksPerSection[i] = new ShortOpenHashSet(); + this.changedBlocksPerSection[i] = new ConcurrentShortHashSet(); } this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos)); diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java index beb7c22cb63021f26c06f91050361e1b25fcc72d..dfb747eba6bf7088af0ff400da169de00a076365 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -1,57 +1,22 @@ package net.minecraft.server.level; -import co.aikar.timings.Timing; // Paper -import com.google.common.collect.ImmutableList; +import com.google.common.collect.*; import com.google.common.collect.ImmutableList.Builder; -import com.google.common.collect.Iterables; -import com.google.common.collect.ComparisonChain; // Paper -import com.google.common.collect.Lists; -import com.google.common.collect.Queues; -import com.google.common.collect.Sets; import com.google.gson.JsonElement; import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.util.Either; import com.mojang.logging.LogUtils; import com.mojang.serialization.DataResult; import com.mojang.serialization.JsonOps; +import io.papermc.paper.util.MCUtil; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ByteMap; -import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2LongMap; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongSet; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.objects.ObjectIterator; -import java.io.IOException; -import java.io.Writer; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.function.IntFunction; -import java.util.function.IntSupplier; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import javax.annotation.Nullable; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceSet; +import it.unimi.dsi.fastutil.objects.ReferenceSets; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; @@ -63,36 +28,19 @@ import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; -import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; -import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket; import net.minecraft.network.protocol.game.DebugPackets; -import io.papermc.paper.util.MCUtil; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.network.ServerPlayerConnection; -import net.minecraft.util.CsvOutput; import net.minecraft.util.Mth; -import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.thread.BlockableEventLoop; -import net.minecraft.util.thread.ProcessorHandle; -import net.minecraft.util.thread.ProcessorMailbox; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.village.poi.PoiManager; import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.GameRules; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LightChunkGetter; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.chunk.*; import net.minecraft.world.level.chunk.storage.ChunkSerializer; import net.minecraft.world.level.chunk.storage.ChunkStorage; import net.minecraft.world.level.entity.ChunkStatusUpdateListener; @@ -101,19 +49,24 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.blending.BlendingData; -import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.phys.Vec3; -import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; -import org.slf4j.Logger; import org.bukkit.craftbukkit.generator.CustomChunkGenerator; -import org.bukkit.entity.Player; -// CraftBukkit end +import org.slf4j.Logger; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.*; public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { @@ -147,13 +100,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider public final StructureTemplateManager structureTemplateManager; // Paper - rewrite chunk system private final String storageName; private final PlayerMap playerMap; - public final Int2ObjectMap entityMap; + public final Map entityMap; private final Long2ByteMap chunkTypeCache; private final Long2LongMap chunkSaveCooldowns; private final Queue unloadQueue; int viewDistance; public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper - public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); + public final ReferenceSet needsChangeBroadcasting = ReferenceSets.synchronize(new ReferenceOpenHashSet<>()); // Paper - rewrite chunk system // Paper start - optimise checkDespawn @@ -295,9 +248,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper - rewrite chunk system this.tickingGenerated = new AtomicInteger(); this.playerMap = new PlayerMap(); - this.entityMap = new Int2ObjectOpenHashMap(); - this.chunkTypeCache = new Long2ByteOpenHashMap(); - this.chunkSaveCooldowns = new Long2LongOpenHashMap(); + this.entityMap = Maps.newConcurrentMap(); + this.chunkTypeCache = Long2ByteMaps.synchronize(new Long2ByteOpenHashMap()); + this.chunkSaveCooldowns = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); this.unloadQueue = Queues.newConcurrentLinkedQueue(); this.structureTemplateManager = structureTemplateManager; Path path = session.getDimensionPath(world.dimension()); @@ -1183,8 +1136,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot // Paper start - ignore and warn about illegal addEntity calls instead of crashing server if (!entity.valid || entity.level != this.level || this.entityMap.containsKey(entity.getId())) { - LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() - + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable()); + LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName() + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : "")); return; } if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets @@ -1209,11 +1161,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ServerPlayer entityplayer = (ServerPlayer) entity; this.updatePlayerStatus(entityplayer, true); - ObjectIterator objectiterator = this.entityMap.values().iterator(); - - while (objectiterator.hasNext()) { - ChunkMap.TrackedEntity playerchunkmap_entitytracker1 = (ChunkMap.TrackedEntity) objectiterator.next(); + for (TrackedEntity playerchunkmap_entitytracker1 : this.entityMap.values()) { if (playerchunkmap_entitytracker1.entity != entityplayer) { playerchunkmap_entitytracker1.updatePlayer(entityplayer); } @@ -1231,11 +1180,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider ServerPlayer entityplayer = (ServerPlayer) entity; this.updatePlayerStatus(entityplayer, false); - ObjectIterator objectiterator = this.entityMap.values().iterator(); - - while (objectiterator.hasNext()) { - ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next(); + for (TrackedEntity playerchunkmap_entitytracker : this.entityMap.values()) { playerchunkmap_entitytracker.removePlayer(entityplayer); } } @@ -1281,7 +1227,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider // Paper end - optimized tracker List list = Lists.newArrayList(); List list1 = this.level.players(); - ObjectIterator objectiterator = this.entityMap.values().iterator(); + Iterator objectiterator = this.entityMap.values().iterator(); //level.timings.tracker1.startTiming(); // Paper // Purpur ChunkMap.TrackedEntity playerchunkmap_entitytracker; diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java index 52cba8f68d274cce106304aef1249a95474d3238..a9ba8adc5f290f6e2820632bdae8e50165595706 100644 --- a/src/main/java/net/minecraft/server/level/DistanceManager.java +++ b/src/main/java/net/minecraft/server/level/DistanceManager.java @@ -1,42 +1,20 @@ package net.minecraft.server.level; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.mojang.datafixers.util.Either; import com.mojang.logging.LogUtils; -import it.unimi.dsi.fastutil.longs.Long2ByteMap; -import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2IntMap; -import it.unimi.dsi.fastutil.longs.Long2IntMaps; -import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.objects.ObjectIterator; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Iterator; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import javax.annotation.Nullable; import net.minecraft.core.SectionPos; import net.minecraft.util.SortedArraySet; -import net.minecraft.util.thread.ProcessorHandle; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.LevelChunk; import org.slf4j.Logger; +import javax.annotation.Nullable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Executor; public abstract class DistanceManager { @@ -52,7 +30,7 @@ public abstract class DistanceManager { private static final int INITIAL_TICKET_LIST_CAPACITY = 4; private static final int ENTITY_TICKING_LEVEL_THRESHOLD = 32; private static final int BLOCK_TICKING_LEVEL_THRESHOLD = 33; - final Long2ObjectMap> playersPerChunk = new Long2ObjectOpenHashMap(); + final Long2ObjectMap> playersPerChunk = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); // Paper - rewrite chunk system public static final int MOB_SPAWN_RANGE = 8; // private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - no longer used //private final TickingTracker tickingTicketsTracker = new TickingTracker(); // Paper - no longer used diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java index 0ae45cf5a084fd412305e8b2f5dabe608b4eb1c1..e311724d2e723115bc9549a61e6206a8aed835d8 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java @@ -4,43 +4,22 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.util.Either; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.function.Supplier; -import javax.annotation.Nullable; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import it.unimi.dsi.fastutil.objects.ObjectSet; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.Packet; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.VisibleForDebug; -import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.GameRules; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.LocalMobCapCalculator; -import net.minecraft.world.level.NaturalSpawner; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.ChunkSource; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.*; +import net.minecraft.world.level.chunk.*; import net.minecraft.world.level.chunk.storage.ChunkScanAccess; import net.minecraft.world.level.entity.ChunkStatusUpdateListener; import net.minecraft.world.level.levelgen.RandomState; @@ -48,7 +27,16 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.LevelData; import net.minecraft.world.level.storage.LevelStorageSource; -import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper + +import javax.annotation.Nullable; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Supplier; public class ServerChunkCache extends ChunkSource { @@ -72,8 +60,7 @@ public class ServerChunkCache extends ChunkSource { @VisibleForDebug private NaturalSpawner.SpawnState lastSpawnState; // Paper start - final com.destroystokyo.paper.util.concurrent.WeakSeqLock loadedChunkMapSeqLock = new com.destroystokyo.paper.util.concurrent.WeakSeqLock(); - final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f); + private final Long2ObjectMap loadedChunkMap = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>(8192, 0.5f)); private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4]; @@ -85,12 +72,7 @@ public class ServerChunkCache extends ChunkSource { } public void addLoadedChunk(LevelChunk chunk) { - this.loadedChunkMapSeqLock.acquireWrite(); - try { - this.loadedChunkMap.put(chunk.coordinateKey, chunk); - } finally { - this.loadedChunkMapSeqLock.releaseWrite(); - } + this.loadedChunkMap.put(chunk.coordinateKey, chunk); // rewrite cache if we have to // we do this since we also cache null chunks @@ -100,13 +82,7 @@ public class ServerChunkCache extends ChunkSource { } public void removeLoadedChunk(LevelChunk chunk) { - this.loadedChunkMapSeqLock.acquireWrite(); - try { - this.loadedChunkMap.remove(chunk.coordinateKey); - } finally { - this.loadedChunkMapSeqLock.releaseWrite(); - } - + this.loadedChunkMap.remove(chunk.coordinateKey); // rewrite cache if we have to // we do this since we also cache null chunks int cacheKey = getChunkCacheKey(chunk.locX, chunk.locZ); @@ -375,22 +351,7 @@ public class ServerChunkCache extends ChunkSource { return this.getChunkAtIfLoadedMainThread(x, z); } - LevelChunk ret = null; - long readlock; - do { - readlock = this.loadedChunkMapSeqLock.acquireRead(); - try { - ret = this.loadedChunkMap.get(k); - } catch (Throwable thr) { - if (thr instanceof ThreadDeath) { - throw (ThreadDeath)thr; - } - // re-try, this means a CME occurred... - continue; - } - } while (!this.loadedChunkMapSeqLock.tryReleaseRead(readlock)); - - return ret; + return this.loadedChunkMap.get(k); } // Paper end // Paper start - async chunk io @@ -451,7 +412,7 @@ public class ServerChunkCache extends ChunkSource { // Paper end com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info //this.level.timings.syncChunkLoad.startTiming(); // Paper // Purpur - chunkproviderserver_b.managedBlock(completablefuture::isDone); + chunkproviderserver_b.managedBlock(completablefuture::isDone); io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system //this.level.timings.syncChunkLoad.stopTiming(); // Paper // Purpur } // Paper @@ -510,6 +471,7 @@ public class ServerChunkCache extends ChunkSource { // Paper start - add isUrgent - old sig left in place for dirty nms plugins return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false); } + private CompletableFuture> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create, boolean isUrgent) { // Paper start - rewrite chunk system io.papermc.paper.util.TickThread.ensureTickThread(this.level, chunkX, chunkZ, "Scheduling chunk load off-main"); @@ -803,7 +765,7 @@ public class ServerChunkCache extends ChunkSource { //gameprofilerfiller.popPush("broadcast"); // Purpur //this.level.timings.broadcastChunkUpdates.startTiming(); // Paper - timing // Purpur if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { - ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); + List copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting); this.chunkMap.needsChangeBroadcasting.clear(); for (ChunkHolder holder : copy) { holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded @@ -848,12 +810,10 @@ public class ServerChunkCache extends ChunkSource { if (chunkMap.playerMobDistanceMap != null && _pufferfish_spawnCountsReady.getAndSet(false)) { net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> { int mapped = distanceManager.getNaturalSpawnChunkCount(); - io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator objectiterator = - level.entityTickList.entities.iterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); + Iterator objectiterator = level.entityTickList.entities.iterator(); gg.pufferfish.pufferfish.util.IterableWrapper wrappedIterator = new gg.pufferfish.pufferfish.util.IterableWrapper<>(objectiterator); lastSpawnState = NaturalSpawner.createState(mapped, wrappedIterator, this::getFullChunk, null, true); - objectiterator.finishedIterating(); _pufferfish_spawnCountsReady.set(true); }); } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 03b8ef3409fd5f7a4d4b06e13cf8eb22b3bbf8a1..39c3aaf91514bd8a2f9f04496e25a6253442939f 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -1,69 +1,35 @@ package net.minecraft.server.level; +import co.aikar.timings.TimingHistory; +import co.earthme.hearse.concurrent.WorkerThread; +import co.earthme.hearse.server.ServerEntityTickHook; import com.google.common.annotations.VisibleForTesting; -import co.aikar.timings.TimingHistory; // Paper import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; +import io.papermc.paper.util.MCUtil; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSets; import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectIterator; -import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.Writer; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Executor; -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import net.minecraft.CrashReport; import net.minecraft.Util; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; +import net.minecraft.core.*; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.protocol.Packet; -import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket; -import net.minecraft.network.protocol.game.ClientboundBlockEventPacket; -import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; -import net.minecraft.network.protocol.game.ClientboundExplodePacket; -import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; -import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; -import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket; -import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; -import net.minecraft.network.protocol.game.ClientboundSoundPacket; -import net.minecraft.network.protocol.game.DebugPackets; +import net.minecraft.network.protocol.game.*; import net.minecraft.resources.ResourceKey; -import io.papermc.paper.util.MCUtil; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerScoreboard; import net.minecraft.server.level.progress.ChunkProgressListener; @@ -71,21 +37,10 @@ import net.minecraft.server.players.SleepStatus; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.TagKey; -import net.minecraft.util.AbortableIterationConsumer; -import net.minecraft.util.CsvOutput; -import net.minecraft.util.Mth; -import net.minecraft.util.ProgressListener; -import net.minecraft.util.Unit; -import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.util.*; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LightningBolt; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.entity.ReputationEventHandler; +import net.minecraft.world.entity.*; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.entity.ai.village.ReputationEventType; import net.minecraft.world.entity.ai.village.poi.PoiManager; @@ -102,17 +57,7 @@ import net.minecraft.world.entity.raid.Raid; import net.minecraft.world.entity.raid.Raids; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.crafting.RecipeManager; -import net.minecraft.world.level.BlockEventData; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.CustomSpawner; -import net.minecraft.world.level.Explosion; -import net.minecraft.world.level.ExplosionDamageCalculator; -import net.minecraft.world.level.ForcedChunksSavedData; -import net.minecraft.world.level.GameRules; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.NaturalSpawner; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.*; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.block.Block; @@ -128,12 +73,10 @@ import net.minecraft.world.level.chunk.storage.EntityStorage; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.end.EndDragonFight; -import net.minecraft.world.level.entity.EntityPersistentStorage; import net.minecraft.world.level.entity.EntityTickList; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.entity.LevelCallback; import net.minecraft.world.level.entity.LevelEntityGetter; -import net.minecraft.world.level.entity.PersistentEntitySectionManager; import net.minecraft.world.level.gameevent.DynamicGameEventListener; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEventDispatcher; @@ -158,21 +101,32 @@ import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.ticks.LevelTicks; -import org.slf4j.Logger; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.WeatherType; import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.generator.CustomWorldChunkManager; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; import org.bukkit.craftbukkit.util.WorldUUID; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.server.MapInitializeEvent; import org.bukkit.event.weather.LightningStrikeEvent; -import org.bukkit.event.world.GenericGameEvent; import org.bukkit.event.world.TimeSkipEvent; -// CraftBukkit end -import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper +import org.slf4j.Logger; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Executor; +import java.util.function.BooleanSupplier; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class ServerLevel extends Level implements WorldGenLevel { @@ -192,7 +146,7 @@ public class ServerLevel extends Level implements WorldGenLevel { public final ServerChunkCache chunkSource; private final MinecraftServer server; public final PrimaryLevelData serverLevelData; // CraftBukkit - type - final EntityTickList entityTickList; + public final EntityTickList entityTickList; //public final PersistentEntitySectionManager entityManager; // Paper - rewrite chunk system private final GameEventDispatcher gameEventDispatcher; public boolean noSave; @@ -204,7 +158,7 @@ public class ServerLevel extends Level implements WorldGenLevel { final Set navigatingMobs; volatile boolean isUpdatingNavigations; protected final Raids raids; - private final ObjectLinkedOpenHashSet blockEvents; + private final Deque blockEvents; private final List blockEventsToReschedule; private boolean handlingTick; private final List customSpawners; @@ -269,7 +223,7 @@ public class ServerLevel extends Level implements WorldGenLevel { public final void loadChunksForMoveAsync(AABB axisalignedbb, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority, java.util.function.Consumer> onLoad) { - if (Thread.currentThread() != this.thread) { + if (Thread.currentThread() != this.thread && !WorkerThread.isWorker()) { this.getChunkSource().mainThreadProcessor.execute(() -> { this.loadChunksForMoveAsync(axisalignedbb, priority, onLoad); }); @@ -536,10 +490,10 @@ public class ServerLevel extends Level implements WorldGenLevel { this.entityTickList = new EntityTickList(); this.blockTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier()); this.fluidTicks = new LevelTicks<>(this::isPositionTickingWithEntitiesLoaded, this.getProfilerSupplier()); - this.navigatingMobs = new ObjectOpenHashSet(); - this.blockEvents = new ObjectLinkedOpenHashSet(); - this.blockEventsToReschedule = new ArrayList(64); - this.dragonParts = new Int2ObjectOpenHashMap(); + this.navigatingMobs = Sets.newConcurrentHashSet(); + this.blockEvents = new ConcurrentLinkedDeque<>(); + this.blockEventsToReschedule = Collections.synchronizedList(new ArrayList(64)); + this.dragonParts = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap()); this.tickTime = flag1; this.server = minecraftserver; this.customSpawners = list; @@ -691,71 +645,15 @@ public class ServerLevel extends Level implements WorldGenLevel { //timings.doSounds.stopTiming(); // Spigot // Purpur this.handlingTick = false; //gameprofilerfiller.pop(); // Purpur - boolean flag = true || !this.players.isEmpty() || !this.getForcedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players - - if (flag) { - this.resetEmptyTime(); - } - - if (flag || this.emptyTime++ < 300) { - //gameprofilerfiller.push("entities"); // Purpur - //timings.tickEntities.startTiming(); // Spigot // Purpur - if (this.dragonFight != null) { - //gameprofilerfiller.push("dragonFight"); // Purpur - this.dragonFight.tick(); - //gameprofilerfiller.pop(); // Purpur - } - - org.spigotmc.ActivationRange.activateEntities(this); // Spigot - //timings.entityTick.startTiming(); // Spigot // Purpur - this.entityTickList.forEach((entity) -> { - entity.activatedPriorityReset = false; // Pufferfish - DAB - if (!entity.isRemoved()) { - if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed - entity.discard(); - } else { - //gameprofilerfiller.push("checkDespawn"); // Purpur - entity.checkDespawn(); - //gameprofilerfiller.pop(); // Purpur - if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list - Entity entity1 = entity.getVehicle(); - - if (entity1 != null) { - if (!entity1.isRemoved() && entity1.hasPassenger(entity)) { - return; - } - - entity.stopRiding(); - } - - //gameprofilerfiller.push("tick"); // Purpur - // Pufferfish start - copied from this.guardEntityTick - try { - this.tickNonPassenger(entity); // Pufferfish - changed - MinecraftServer.getServer().executeMidTickTasks(); // Tuinity - execute chunk tasks mid tick - } catch (Throwable throwable) { - if (throwable instanceof ThreadDeath) throw throwable; // Paper - // Paper start - Prevent tile entity and entity crashes - final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ()); - MinecraftServer.LOGGER.error(msg, throwable); - getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(msg, throwable))); - entity.discard(); - // Paper end - } - // Pufferfish end - //gameprofilerfiller.pop(); // Purpur - } - } - } - }); - //timings.entityTick.stopTiming(); // Spigot // Purpur - //timings.tickEntities.stopTiming(); // Spigot // Purpur - //gameprofilerfiller.pop(); // Purpur - this.tickBlockEntities(); + this.resetEmptyTime(); + if (this.dragonFight != null) { + this.dragonFight.tick(); } - - //gameprofilerfiller.push("entityManagement"); // Purpur - //this.entityManager.tick(); // Paper - rewrite chunk system + org.spigotmc.ActivationRange.activateEntities(this); // Spigot + this.entityTickList.forEach((entity) -> { + ServerEntityTickHook.callAsyncEntityTick(entity,this); + }); + this.tickBlockEntities(); } @Override @@ -794,7 +692,7 @@ public class ServerLevel extends Level implements WorldGenLevel { } - private boolean shouldDiscardEntity(Entity entity) { + public boolean shouldDiscardEntity(Entity entity) { return !this.server.isSpawningAnimals() && (entity instanceof Animal || entity instanceof WaterAnimal) ? true : !this.server.areNpcsEnabled() && entity instanceof Npc; } @@ -993,7 +891,7 @@ public class ServerLevel extends Level implements WorldGenLevel { if (this.canSleepThroughNights()) { if (!this.getServer().isSingleplayer() || this.getServer().isPublished()) { int i = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); - MutableComponent ichatmutablecomponent; + Component ichatmutablecomponent; if (this.sleepStatus.areEnoughSleeping(i)) { ichatmutablecomponent = Component.translatable("sleep.skipping_night"); @@ -1228,13 +1126,10 @@ public class ServerLevel extends Level implements WorldGenLevel { } else { entity.inactiveTick(); } // Paper - EAR 2 //this.getProfiler().pop(); // Purpur //} finally { timer.stopTiming(); } // Paper - timings // Purpur - Iterator iterator = entity.getPassengers().iterator(); - - while (iterator.hasNext()) { - Entity entity1 = (Entity) iterator.next(); - this.tickPassenger(entity, entity1); - } + for (Entity entity1 : entity.getPassengers()) { + this.tickPassenger(entity, entity1); + } // } finally { timer.stopTiming(); } // Paper - timings - move up // Paper start - log detailed entity tick information } finally { @@ -1273,11 +1168,8 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper end - EAR 2 //gameprofilerfiller.pop(); // Purpur - Iterator iterator = passenger.getPassengers().iterator(); - - while (iterator.hasNext()) { - Entity entity2 = (Entity) iterator.next(); + for (Entity entity2 : passenger.getPassengers()) { this.tickPassenger(passenger, entity2); } @@ -1661,56 +1553,56 @@ public class ServerLevel extends Level implements WorldGenLevel { @Override public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { - if (this.isUpdatingNavigations) { - String s = "recursive call to sendBlockUpdated"; + synchronized (this.navigatingMobs){ + if (this.isUpdatingNavigations) { + return; + } - Util.logAndPauseIfInIde("recursive call to sendBlockUpdated", new IllegalStateException("recursive call to sendBlockUpdated")); - } + this.getChunkSource().blockChanged(pos); + if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates + VoxelShape voxelshape = oldState.getCollisionShape(this, pos); + VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); - this.getChunkSource().blockChanged(pos); - if(this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates - VoxelShape voxelshape = oldState.getCollisionShape(this, pos); - VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); + if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { + List list = new ObjectArrayList(); + Iterator iterator = this.navigatingMobs.iterator(); - if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) { - List list = new ObjectArrayList(); - Iterator iterator = this.navigatingMobs.iterator(); + while (iterator.hasNext()) { + // CraftBukkit start - fix SPIGOT-6362 + Mob entityinsentient; + try { + entityinsentient = (Mob) iterator.next(); + } catch (java.util.ConcurrentModificationException ex) { + // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register + // In this case we just run the update again across all the iterators as the chunk will then be loaded + // As this is a relative edge case it is much faster than copying navigators (on either read or write) + this.sendBlockUpdated(pos, oldState, newState, flags); + return; + } + // CraftBukkit end + PathNavigation navigationabstract = entityinsentient.getNavigation(); - while (iterator.hasNext()) { - // CraftBukkit start - fix SPIGOT-6362 - Mob entityinsentient; - try { - entityinsentient = (Mob) iterator.next(); - } catch (java.util.ConcurrentModificationException ex) { - // This can happen because the pathfinder update below may trigger a chunk load, which in turn may cause more navigators to register - // In this case we just run the update again across all the iterators as the chunk will then be loaded - // As this is a relative edge case it is much faster than copying navigators (on either read or write) - this.sendBlockUpdated(pos, oldState, newState, flags); - return; - } - // CraftBukkit end - PathNavigation navigationabstract = entityinsentient.getNavigation(); + if (navigationabstract.shouldRecomputePath(pos)) { + list.add(navigationabstract); + } + } - if (navigationabstract.shouldRecomputePath(pos)) { - list.add(navigationabstract); - } - } + try { + this.isUpdatingNavigations = true; + iterator = list.iterator(); - try { - this.isUpdatingNavigations = true; - iterator = list.iterator(); + while (iterator.hasNext()) { + PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); - while (iterator.hasNext()) { - PathNavigation navigationabstract1 = (PathNavigation) iterator.next(); + navigationabstract1.recomputePath(); + } + } finally { + this.isUpdatingNavigations = false; + } - navigationabstract1.recomputePath(); } - } finally { - this.isUpdatingNavigations = false; - } - + } // Paper } - } // Paper } @Override @@ -1777,10 +1669,8 @@ public class ServerLevel extends Level implements WorldGenLevel { private void runBlockEvents() { this.blockEventsToReschedule.clear(); - - while (!this.blockEvents.isEmpty()) { - BlockEventData blockactiondata = (BlockEventData) this.blockEvents.removeFirst(); - + BlockEventData blockactiondata; + while ((blockactiondata = (BlockEventData) this.blockEvents.pollFirst())!=null) { if (this.shouldTickBlocksAt(blockactiondata.pos())) { if (this.doBlockEvent(blockactiondata)) { this.server.getPlayerList().broadcast((Player) null, (double) blockactiondata.pos().getX(), (double) blockactiondata.pos().getY(), (double) blockactiondata.pos().getZ(), 64.0D, this.dimension(), new ClientboundBlockEventPacket(blockactiondata.pos(), blockactiondata.block(), blockactiondata.paramA(), blockactiondata.paramB())); diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java index 660693c6dc0ef86f4013df980b6d0c11c03e46cd..1ea9699ce1f77a551a45fc06dad55df4cc1a4f4d 100644 --- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java +++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java @@ -1,14 +1,9 @@ package net.minecraft.server.level; -import com.mojang.datafixers.util.Pair; +import ca.spottedleaf.starlight.common.light.StarLightEngine; import com.mojang.logging.LogUtils; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; -import it.unimi.dsi.fastutil.objects.ObjectListIterator; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.IntSupplier; -import javax.annotation.Nullable; +import io.papermc.paper.util.CoordinateUtils; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -17,21 +12,17 @@ import net.minecraft.util.thread.ProcessorMailbox; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.DataLayer; -import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.LightChunkGetter; +import net.minecraft.world.level.lighting.LayerLightEventListener; import net.minecraft.world.level.lighting.LevelLightEngine; import org.slf4j.Logger; -// Paper start -import ca.spottedleaf.starlight.common.light.StarLightEngine; -import io.papermc.paper.util.CoordinateUtils; +import javax.annotation.Nullable; +import java.util.concurrent.CompletableFuture; +import java.util.function.IntSupplier; import java.util.function.Supplier; -import net.minecraft.world.level.lighting.LayerLightEventListener; -import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.longs.LongIterator; -import net.minecraft.world.level.chunk.ChunkStatus; // Paper end public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable { @@ -145,20 +136,30 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl return; } - final int references = this.chunksBeingWorkedOn.addTo(key, 1); + int references; + synchronized (this.chunksBeingWorkedOn){ + references = this.chunksBeingWorkedOn.addTo(key, 1); + } if (references == 0) { final ChunkPos pos = new ChunkPos(chunkX, chunkZ); world.getChunkSource().addRegionTicket(ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); } updateFuture.thenAcceptAsync((final Void ignore) -> { - final int newReferences = this.chunksBeingWorkedOn.get(key); + int newReferences; + synchronized (this.chunksBeingWorkedOn){ + newReferences = this.chunksBeingWorkedOn.get(key); + } if (newReferences == 1) { - this.chunksBeingWorkedOn.remove(key); + synchronized (this.chunksBeingWorkedOn){ + this.chunksBeingWorkedOn.remove(key); + } final ChunkPos pos = new ChunkPos(chunkX, chunkZ); world.getChunkSource().removeRegionTicket(ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET, pos, 0, pos); } else { - this.chunksBeingWorkedOn.put(key, newReferences - 1); + synchronized (this.chunksBeingWorkedOn){ + this.chunksBeingWorkedOn.put(key, newReferences - 1); + } } }, world.getChunkSource().chunkMap.mainThreadExecutor).whenComplete((final Void ignore, final Throwable thr) -> { if (thr != null) { diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java index 50a9f33aa31e9273c7c52d4bb2b02f0f884f7ba5..d5802cfe08f92b55ff1fd41648abda9ef2b7dd20 100644 --- a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java +++ b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java @@ -10,12 +10,14 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collector; import java.util.stream.Collectors; public class ClassInstanceMultiMap extends AbstractCollection { - private final Map, List> byClass = Maps.newHashMap(); + private final Map, List> byClass = Maps.newConcurrentMap(); private final Class baseClass; - private final List allInstances = Lists.newArrayList(); + private final List allInstances = Lists.newCopyOnWriteArrayList(); public ClassInstanceMultiMap(Class elementType) { this.baseClass = elementType; @@ -59,12 +61,16 @@ public class ClassInstanceMultiMap extends AbstractCollection { throw new IllegalArgumentException("Don't know how to search for " + type); } else { List list = this.byClass.computeIfAbsent(type, (typeClass) -> { - return this.allInstances.stream().filter(typeClass::isInstance).collect(Collectors.toList()); + return this.allInstances.stream().filter(typeClass::isInstance).collect(toList()); }); - return Collections.unmodifiableCollection(list); + return (Collection) Collections.unmodifiableCollection(list); } } + public static Collector> toList() { + return Collectors.toCollection(CopyOnWriteArrayList::new); + } + @Override public Iterator iterator() { return (Iterator)(this.allInstances.isEmpty() ? Collections.emptyIterator() : Iterators.unmodifiableIterator(this.allInstances.iterator())); diff --git a/src/main/java/net/minecraft/util/ThreadingDetector.java b/src/main/java/net/minecraft/util/ThreadingDetector.java index b6e98aaebe57453b8eceaa633a989aa24409830f..60162cccf765800c6172d1544f2cd9bcf30cbd97 100644 --- a/src/main/java/net/minecraft/util/ThreadingDetector.java +++ b/src/main/java/net/minecraft/util/ThreadingDetector.java @@ -17,7 +17,7 @@ import org.slf4j.Logger; public class ThreadingDetector { private static final Logger LOGGER = LogUtils.getLogger(); private final String name; - private final Semaphore lock = new Semaphore(1); + private final Semaphore lock = new Semaphore(255); private final Lock stackTraceLock = new ReentrantLock(); @Nullable private volatile Thread threadThatFailedToAcquire; diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java index 83701fbfaa56a232593ee8f11a3afb8941238bfa..0eadacb873658a0c7bd9ab24f191bc75eaebcaca 100644 --- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java @@ -3,11 +3,15 @@ package net.minecraft.util.thread; import com.google.common.collect.ImmutableList; import com.google.common.collect.Queues; import com.mojang.logging.LogUtils; + +import java.util.Deque; import java.util.List; import java.util.Queue; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executor; import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.locks.StampedLock; import java.util.function.BooleanSupplier; import java.util.function.Supplier; import net.minecraft.util.profiling.metrics.MetricCategory; @@ -19,7 +23,8 @@ import org.slf4j.Logger; public abstract class BlockableEventLoop implements ProfilerMeasured, ProcessorHandle, Executor { private final String name; private static final Logger LOGGER = LogUtils.getLogger(); - private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); + private final Deque pendingRunnables = new ConcurrentLinkedDeque<>(); + private final StampedLock lock = new StampedLock(); private int blockingCount; protected BlockableEventLoop(String name) { @@ -117,13 +122,14 @@ public abstract class BlockableEventLoop implements Profiler } public boolean pollTask() { - R runnable = this.pendingRunnables.peek(); + R runnable = this.pendingRunnables.poll(); if (runnable == null) { return false; } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { + this.pendingRunnables.addFirst(runnable); return false; } else { - this.doRunTask(this.pendingRunnables.remove()); + this.doRunTask(runnable); return true; } } diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index 8f3f7a4c2429dd3c9060a1d2f412e41a5448bf4c..4216b4bba7c601660b7c39733f626f95d9b20507 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -24,6 +24,8 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.Nullable; + +import it.unimi.dsi.fastutil.objects.Object2DoubleMaps; import net.minecraft.BlockUtil; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; @@ -164,76 +166,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } // Paper start - public static RandomSource SHARED_RANDOM = new RandomRandomSource(); - private static final class RandomRandomSource extends java.util.Random implements net.minecraft.world.level.levelgen.BitRandomSource { - private boolean locked = false; - - @Override - public synchronized void setSeed(long seed) { - if (locked) { - LOGGER.error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); - } else { - super.setSeed(seed); - locked = true; - } - } - - @Override - public RandomSource fork() { - return new net.minecraft.world.level.levelgen.LegacyRandomSource(this.nextLong()); - } - - @Override - public net.minecraft.world.level.levelgen.PositionalRandomFactory forkPositional() { - return new net.minecraft.world.level.levelgen.LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong()); - } - - // these below are added to fix reobf issues that I don't wanna deal with right now - @Override - public int next(int bits) { - return super.next(bits); - } - - @Override - public int nextInt(int origin, int bound) { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(origin, bound); - } - - @Override - public long nextLong() { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextLong(); - } - - @Override - public int nextInt() { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(); - } - - @Override - public int nextInt(int bound) { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(bound); - } - - @Override - public boolean nextBoolean() { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextBoolean(); - } - - @Override - public float nextFloat() { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextFloat(); - } - - @Override - public double nextDouble() { - return net.minecraft.world.level.levelgen.BitRandomSource.super.nextDouble(); - } - - @Override - public double nextGaussian() { - return super.nextGaussian(); - } - } + public static RandomSource SHARED_RANDOM = RandomSource.create(); // Paper end public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper @@ -580,14 +513,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { this.nextStep = 1.0F; this.random = SHARED_RANDOM; // Paper this.remainingFireTicks = -this.getFireImmuneTicks(); - this.fluidHeight = new Object2DoubleArrayMap(2); - this.fluidOnEyes = new HashSet(); + this.fluidHeight = Object2DoubleMaps.synchronize(new Object2DoubleArrayMap(2)); + this.fluidOnEyes = Sets.newConcurrentHashSet(); this.firstTick = true; this.levelCallback = EntityInLevelCallback.NULL; this.packetPositionCodec = new VecDeltaCodec(); this.uuid = Mth.createInsecureUUID(this.random); this.stringUUID = this.uuid.toString(); - this.tags = Sets.newHashSet(); + this.tags = Sets.newConcurrentHashSet(); this.pistonDeltas = new double[]{0.0D, 0.0D, 0.0D}; this.feetBlockState = null; this.type = type; @@ -4434,6 +4367,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { // Paper start this.setPosRaw(x, y, z, false); } + public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { // Paper start - block invalid positions if (!checkPosition(this, x, y, z)) { @@ -4441,12 +4375,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { } // Paper end - block invalid positions // Paper end - // Paper start - rewrite chunk system - if (this.updatingSectionStatus) { - LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable()); - return; - } - // Paper end - rewrite chunk system // Paper start - fix MC-4 if (this instanceof ItemEntity) { if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) { @@ -4562,10 +4490,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { public final void setRemoved(Entity.RemovalReason reason) { // Paper start - rewrite chunk system io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot remove entity off-main"); - if (this.updatingSectionStatus) { - LOGGER.warn("Entity " + this + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable()); - return; - } // Paper end - rewrite chunk system if (this.removalReason == null) { this.removalReason = reason; diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java index 93c32dd39693b37efaa05af0486e1bdd298661f3..c72920a17178059a29d21e96aeef398f6e0bbbdc 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; import javax.annotation.Nullable; import net.minecraft.BlockUtil; @@ -181,7 +182,7 @@ public abstract class LivingEntity extends Entity { public static final float EXTRA_RENDER_CULLING_SIZE_WITH_BIG_HAT = 0.5F; private final AttributeMap attributes; public CombatTracker combatTracker = new CombatTracker(this); - public final Map activeEffects = Maps.newHashMap(); + public final Map activeEffects = Maps.newConcurrentMap(); private final NonNullList lastHandItemStacks; private final NonNullList lastArmorItemStacks; public boolean swinging; @@ -257,7 +258,7 @@ public abstract class LivingEntity extends Entity { // CraftBukkit start public int expToDrop; public boolean forceDrops; - public ArrayList drops = new ArrayList(); + public List drops = new CopyOnWriteArrayList<>(); public final org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes; public boolean collides = true; public Set collidableExemptions = new HashSet<>(); @@ -875,7 +876,7 @@ public abstract class LivingEntity extends Entity { // CraftBukkit start private boolean isTickingEffects = false; - private List effectsToProcess = Lists.newArrayList(); + private List effectsToProcess = Lists.newCopyOnWriteArrayList(); private static class ProcessableEffect { @@ -1779,7 +1780,7 @@ public abstract class LivingEntity extends Entity { } }); // Paper end this.postDeathDropItems(deathEvent); // Paper - this.drops = new ArrayList<>(); + this.drops = new CopyOnWriteArrayList<>(); // CraftBukkit end // this.dropInventory();// CraftBukkit - moved up @@ -1963,6 +1964,10 @@ public abstract class LivingEntity extends Entity { BlockPos blockposition = this.blockPosition(); BlockState iblockdata = this.getFeetBlockState(); + if (iblockdata == null){ + return false; + } + if (iblockdata.is(BlockTags.CLIMBABLE)) { this.lastClimbablePos = Optional.of(blockposition); return true; diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java index d3827215ef19f6e1e63f846d91ed00525a318c7a..71e44f4aa64b29610f424ea91a9b54b56a155736 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/LongJumpToRandomPos.java @@ -9,6 +9,8 @@ import java.util.function.BiPredicate; import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; + +import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvent; @@ -40,7 +42,7 @@ public class LongJumpToRandomPos extends Behavior { protected final int maxLongJumpHeight; protected final int maxLongJumpWidth; protected final float maxJumpVelocity; - protected List jumpCandidates = Lists.newArrayList(); + protected List jumpCandidates = Lists.newCopyOnWriteArrayList(); protected Optional initialPosition = Optional.empty(); @Nullable protected Vec3 chosenJump; @@ -103,7 +105,7 @@ public class LongJumpToRandomPos extends Behavior { return !blockPos2.equals(blockPos); }).map((blockPos2) -> { return new LongJumpToRandomPos.PossibleJump(blockPos2.immutable(), Mth.ceil(blockPos.distSqr(blockPos2))); - }).collect(Collectors.toCollection(Lists::newArrayList)); + }).collect(Collectors.toCollection(Lists::newCopyOnWriteArrayList)); } @Override diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java index fe3ab3d388f0481fb0db06b7f730f868dbf8e8a5..ac006bacbe8715e5c272c69afd1edab45a6511e8 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/ShufflingList.java @@ -25,7 +25,7 @@ public class ShufflingList implements Iterable { public ShufflingList(boolean isUnsafe) { this.isUnsafe = isUnsafe; // Paper end - this.entries = Lists.newArrayList(); + this.entries = Lists.newCopyOnWriteArrayList(); } private ShufflingList(List> list) { @@ -35,7 +35,7 @@ public class ShufflingList implements Iterable { private ShufflingList(List> list, boolean isUnsafe) { this.isUnsafe = isUnsafe; // Paper end - this.entries = Lists.newArrayList(list); + this.entries = Lists.newCopyOnWriteArrayList(list); } public static Codec> codec(Codec codec) { @@ -44,12 +44,12 @@ public class ShufflingList implements Iterable { }); } - public ShufflingList add(U data, int weight) { + public synchronized ShufflingList add(U data, int weight) { this.entries.add(new ShufflingList.WeightedEntry<>(data, weight)); return this; } - public ShufflingList shuffle() { + public synchronized ShufflingList shuffle() { // Paper start - make concurrent safe, work off a clone of the list List> list = this.isUnsafe ? Lists.newArrayList(this.entries) : this.entries; list.forEach(entry -> entry.setRandom(this.random.nextFloat())); @@ -58,17 +58,17 @@ public class ShufflingList implements Iterable { // Paper end } - public Stream stream() { + public synchronized Stream stream() { return this.entries.stream().map(ShufflingList.WeightedEntry::getData); } @Override - public Iterator iterator() { + public synchronized Iterator iterator() { return Iterators.transform(this.entries.iterator(), ShufflingList.WeightedEntry::getData); } @Override - public String toString() { + public synchronized String toString() { return "ShufflingList[" + this.entries + "]"; } @@ -90,16 +90,16 @@ public class ShufflingList implements Iterable { this.randWeight = -Math.pow((double)random, (double)(1.0F / (float)this.weight)); } - public T getData() { + public synchronized T getData() { return this.data; } - public int getWeight() { + public synchronized int getWeight() { return this.weight; } @Override - public String toString() { + public synchronized String toString() { return this.weight + ":" + this.data; } diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java index 02978315bc2b828cc603ce7478408f3f82c249c2..fc26edc5082f701e6450ca9abf78423840cd773c 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -3,11 +3,8 @@ package net.minecraft.world.entity.ai.goal; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; import com.mojang.logging.LogUtils; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; + +import java.util.*; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Stream; @@ -27,8 +24,8 @@ public class GoalSelector { return false; } }; - private final Map lockedFlags = new EnumMap<>(Goal.Flag.class); - private final Set availableGoals = Sets.newLinkedHashSet(); + private final Map lockedFlags = Collections.synchronizedMap(new EnumMap<>(Goal.Flag.class)); + private final Set availableGoals = Sets.newConcurrentHashSet(); private final Supplier profiler; private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be. private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector @@ -114,14 +111,7 @@ public class GoalSelector { } } - Iterator> iterator = this.lockedFlags.entrySet().iterator(); - - while(iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (!entry.getValue().isRunning()) { - iterator.remove(); - } - } + this.lockedFlags.entrySet().removeIf(entry -> !entry.getValue().isRunning()); //profilerFiller.pop(); // Purpur //profilerFiller.push("goalUpdate"); // Purpur diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java index 8db20db72cd51046213625fac46c35854c59ec5d..4d40526cc90c19ff5a1569c8d6d828a0d0b73ccb 100644 --- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java @@ -3,6 +3,7 @@ package net.minecraft.world.entity.ai.sensing; import com.google.common.collect.ImmutableSet; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.longs.Long2LongMap; +import it.unimi.dsi.fastutil.longs.Long2LongMaps; import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; import java.util.Optional; import java.util.Set; @@ -23,7 +24,7 @@ public class NearestBedSensor extends Sensor { private static final int CACHE_TIMEOUT = 40; private static final int BATCH_SIZE = 5; private static final int RATE = 20; - private final Long2LongMap batchCache = new Long2LongOpenHashMap(); + private final Long2LongMap batchCache = Long2LongMaps.synchronize(new Long2LongOpenHashMap()); private int triedCount; private long lastUpdate; diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java index 9babe636176da3c40598eb5bdac0919a1704eaa0..58c6b1f67aedf5ab2167fd070604fc0d8f710435 100644 --- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java +++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensing.java @@ -2,13 +2,14 @@ package net.minecraft.world.entity.ai.sensing; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSets; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Mob; public class Sensing { private final Mob mob; - private final IntSet seen = new IntOpenHashSet(); - private final IntSet unseen = new IntOpenHashSet(); + private final IntSet seen = IntSets.synchronize(new IntOpenHashSet()); + private final IntSet unseen = IntSets.synchronize(new IntOpenHashSet()); public Sensing(Mob owner) { this.mob = owner; diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java index 9f138bc471b5c2a4fa813ff943dbe34018b8df74..5c8a90f8536c9291df5891d8c75de963b75ec4bd 100644 --- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java +++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java @@ -7,6 +7,7 @@ import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import java.util.List; import java.util.Map; @@ -25,8 +26,9 @@ import org.slf4j.Logger; public class PoiSection { private static final Logger LOGGER = LogUtils.getLogger(); - private final Short2ObjectMap records = new Short2ObjectOpenHashMap<>(); - private final Map, Set> byType = Maps.newHashMap(); public final Map, Set> getData() { return this.byType; } // Paper - public accessor + private final Short2ObjectMap records = Short2ObjectMaps.synchronize(new Short2ObjectOpenHashMap<>()); + private final Map, Set> byType = Maps.newConcurrentMap(); + public final Map, Set> getData() { return this.byType; } // Paper - public accessor private final Runnable setDirty; private boolean isValid; public final Optional noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java index c58496c84b2b3f86890050813041fa49711f3a01..3e8b68b4390a6086b1b1983fe6f8b545c2847ef8 100644 --- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java @@ -4,6 +4,8 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nullable; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; @@ -239,21 +241,24 @@ public class ItemEntity extends Entity { this.setDeltaMovement(vec3d.x * 0.949999988079071D, vec3d.y + (double) (vec3d.y < 0.05999999865889549D ? 5.0E-4F : 0.0F), vec3d.z * 0.949999988079071D); } + private final Lock mergeLock = new ReentrantLock(); + private void mergeWithNeighbours() { - if (this.isMergable()) { - // Spigot start - double radius = level.spigotConfig.itemMerge; - List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> { - // Spigot end - return entityitem != this && entityitem.isMergable(); - }); - Iterator iterator = list.iterator(); - - while (iterator.hasNext()) { - ItemEntity entityitem = (ItemEntity) iterator.next(); - - if (entityitem.isMergable()) { - // Paper Start - Fix items merging through walls + if (!this.mergeLock.tryLock()){ + return; + } + try { + if (this.isMergable()) { + // Spigot start + double radius = level.spigotConfig.itemMerge; + List list = this.level.getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, radius - 0.5D, radius), (entityitem) -> { + // Spigot end + return entityitem != this && entityitem.isMergable(); + }); + + for (ItemEntity entityitem : list) { + if (entityitem.isMergable()) { + // Paper Start - Fix items merging through walls if (this.level.paperConfig().fixes.fixItemsMergingThroughWalls) { // Pufferfish start - skip the allocations /* @@ -263,17 +268,19 @@ public class ItemEntity extends Entity { if (rayTraceResult.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK) continue; */ if (level.rayTraceDirect(this.position(), entityitem.position(), net.minecraft.world.phys.shapes.CollisionContext.of(this)) == - net.minecraft.world.phys.HitResult.Type.BLOCK) continue; + net.minecraft.world.phys.HitResult.Type.BLOCK) continue; // Pufferfish end } - // Paper End - this.tryToMerge(entityitem); - if (this.isRemoved()) { - break; + // Paper End + this.tryToMerge(entityitem); + if (this.isRemoved()) { + break; + } } } } - + }finally { + this.mergeLock.unlock(); } } diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java index 8450a22b0fc6e8dc5cad0f61ac52a82b3cd3791e..c55f98fb2a6aade8e0a6f60248f4e29f66f0c193 100644 --- a/src/main/java/net/minecraft/world/item/ItemStack.java +++ b/src/main/java/net/minecraft/world/item/ItemStack.java @@ -248,7 +248,9 @@ public final class ItemStack { } private void updateEmptyCacheFlag() { - if (this.emptyCacheFlag && this == ItemStack.EMPTY) throw new AssertionError("TRAP"); // CraftBukkit + if (this.emptyCacheFlag && this == ItemStack.EMPTY){ + return; + }//throw new AssertionError("TRAP"); // CraftBukkit this.emptyCacheFlag = false; this.emptyCacheFlag = this.isEmpty(); } diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java index c0d39afe5b80159ed9aaca4ddd4763d707882f2e..a4e031682329e127a61a9fa03a7383cd3e58d503 100644 --- a/src/main/java/net/minecraft/world/level/Explosion.java +++ b/src/main/java/net/minecraft/world/level/Explosion.java @@ -4,12 +4,15 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.ObjectListIterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; + +import it.unimi.dsi.fastutil.objects.ObjectLists; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; @@ -81,9 +84,9 @@ public class Explosion { } public Explosion(Level world, @Nullable Entity entity, @Nullable DamageSource damageSource, @Nullable ExplosionDamageCalculator behavior, double x, double y, double z, float power, boolean createFire, Explosion.BlockInteraction destructionType) { - this.random = RandomSource.create(); - this.toBlow = new ObjectArrayList(); - this.hitPlayers = Maps.newHashMap(); + this.random = RandomSource.createThreadSafe(); + this.toBlow = new ObjectArrayList<>(); + this.hitPlayers = Maps.newConcurrentMap(); this.level = world; this.source = entity; this.radius = (float) Math.max(power, 0.0); // CraftBukkit - clamp bad values @@ -396,14 +399,10 @@ public class Explosion { } if (this.fire) { - ObjectListIterator objectlistiterator1 = this.toBlow.iterator(); - - while (objectlistiterator1.hasNext()) { - BlockPos blockposition2 = (BlockPos) objectlistiterator1.next(); - + for (BlockPos blockposition2 : this.toBlow) { if (this.random.nextInt(3) == 0 && this.level.getBlockState(blockposition2).isAir() && this.level.getBlockState(blockposition2.below()).isSolidRender(this.level, blockposition2.below())) { // CraftBukkit start - Ignition by explosion - if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) { + if (!CraftEventFactory.callBlockIgniteEvent(this.level, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), this).isCancelled()) { this.level.setBlockAndUpdate(blockposition2, BaseFireBlock.getState(this.level, blockposition2)); } // CraftBukkit end diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index 958e0ee29915bddde2cb8ebfd578448b83e2b149..4a75c2895f4f5e3229be3c7b177445611a1e70b6 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -2,10 +2,12 @@ package net.minecraft.world.level; import co.aikar.timings.Timing; import co.aikar.timings.Timings; +import co.earthme.hearse.concurrent.WorkerThread; import com.destroystokyo.paper.event.server.ServerExceptionEvent; import com.destroystokyo.paper.exception.ServerInternalException; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.mojang.serialization.Codec; import java.io.IOException; import java.util.Iterator; @@ -178,7 +180,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { private org.spigotmc.TickLimiter entityLimiter; private org.spigotmc.TickLimiter tileLimiter; private int tileTickPosition; - public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions + public final Map explosionDensityCache = Maps.newConcurrentMap(); // Paper - Optimize explosions public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here // Paper start - fix and optimise world upgrading @@ -1116,7 +1118,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } // Paper end // CraftBukkit end - return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system + return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() && !WorkerThread.isWorker() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system } public void setBlockEntity(BlockEntity blockEntity) { diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java index e254b2d04e4fc1dc76c26f61ea38aeb27755143f..948abd93c64a5b3679f3552945d7a9a2edaf7c3f 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java @@ -17,6 +17,9 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; import javax.annotation.Nullable; + +import it.unimi.dsi.fastutil.shorts.ShortLists; +import net.himeki.mcmtfabric.parallelised.fastutil.ConcurrentShortHashSet; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; @@ -384,7 +387,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom public static ShortList getOrCreateOffsetList(ShortList[] lists, int index) { if (lists[index] == null) { - lists[index] = new ShortArrayList(); + lists[index] = ShortLists.synchronize(new ShortArrayList()); } return lists[index]; diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java index 2e922bb844bc147224a60ef2aae33a0125e6ca4a..f1aae5aa6d2951e21e5e0f84fc87117d41876695 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -88,7 +88,7 @@ public class LevelChunk extends ChunkAccess { private Supplier fullStatus; @Nullable private LevelChunk.PostLoadProcessor postLoad; - private final Int2ObjectMap gameEventListenerRegistrySections; + private final Map gameEventListenerRegistrySections; private final LevelChunkTicks blockTicks; private final LevelChunkTicks fluidTicks; @@ -114,10 +114,10 @@ public class LevelChunk extends ChunkAccess { this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world)); this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world)); // Paper end - rewrite light engine - this.tickersInLevel = Maps.newHashMap(); + this.tickersInLevel = Maps.newConcurrentMap(); this.clientLightReady = false; this.level = (ServerLevel) world; // CraftBukkit - type - this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap(); + this.gameEventListenerRegistrySections = Maps.newConcurrentMap(); Heightmap.Types[] aheightmap_type = Heightmap.Types.values(); int j = aheightmap_type.length; @@ -1075,10 +1075,8 @@ public class LevelChunk extends ChunkAccess { for (int i = 0; i < this.postProcessing.length; ++i) { if (this.postProcessing[i] != null) { - ShortListIterator shortlistiterator = this.postProcessing[i].iterator(); - while (shortlistiterator.hasNext()) { - Short oshort = (Short) shortlistiterator.next(); + for (Short oshort : this.postProcessing[i]) { BlockPos blockposition = ProtoChunk.unpackOffsetCoordinates(oshort, this.getSectionYFromSectionIndex(i), chunkcoordintpair); BlockState iblockdata = this.getBlockState(blockposition); FluidState fluid = iblockdata.getFluidState(); diff --git a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java index d45d832232be5017dde53816191c2b1830a0da32..f73f78e2f7c6e3eae66f7608a92854b3246e153d 100644 --- a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java +++ b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java @@ -8,13 +8,15 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.util.Map; import java.util.UUID; import javax.annotation.Nullable; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import net.minecraft.util.AbortableIterationConsumer; import org.slf4j.Logger; public class EntityLookup { private static final Logger LOGGER = LogUtils.getLogger(); - private final Int2ObjectMap byId = new Int2ObjectLinkedOpenHashMap<>(); - private final Map byUuid = Maps.newHashMap(); + private final Int2ObjectMap byId = Int2ObjectMaps.synchronize(new Int2ObjectLinkedOpenHashMap<>()); + private final Map byUuid = Maps.newConcurrentMap(); public void getEntities(EntityTypeTest filter, AbortableIterationConsumer consumer) { for(T entityAccess : this.byId.values()) { diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java index 57fcf3910f45ce371ac2e237b277b1034caaac4e..2a14b665437336aa32ca14fb2137d5bb400e2e42 100644 --- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java +++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java @@ -1,49 +1,31 @@ package net.minecraft.world.level.entity; -import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; -import java.util.function.Consumer; -import javax.annotation.Nullable; +import com.google.common.collect.Lists; import net.minecraft.world.entity.Entity; +import java.util.List; +import java.util.function.Consumer; public class EntityTickList { - public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking? // Pufferfish - private->public - - private void ensureActiveIsNotIterated() { - // Paper - replace with better logic, do not delay removals - - } + public final List entities = Lists.newCopyOnWriteArrayList(); public void add(Entity entity) { io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper - this.ensureActiveIsNotIterated(); - this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions + this.entities.add(entity); } public void remove(Entity entity) { io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper - this.ensureActiveIsNotIterated(); - this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions + this.entities.remove(entity); } public boolean contains(Entity entity) { - return this.entities.contains(entity); // Paper - replace with better logic, do not delay removals/additions + return this.entities.contains(entity); } public void forEach(Consumer action) { io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper - // Paper start - replace with better logic, do not delay removals/additions - // To ensure nothing weird happens with dimension travelling, do not iterate over new entries... - // (by dfl iterator() is configured to not iterate over new entries) - io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator iterator = this.entities.iterator(); - try { - while (iterator.hasNext()) { - action.accept(iterator.next()); - } - } finally { - iterator.finishedIterating(); + for (Entity entity : this.entities) { + action.accept(entity); } - // Paper end - replace with better logic, do not delay removals/additions } } diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java index a77985b2dd7137d8eea03909403fc08e89376d73..6bcbbbfc39432076a3d7714ecc2d05d9112d405c 100644 --- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java @@ -4,12 +4,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Queues; import com.google.common.collect.Sets; import com.mojang.logging.LogUtils; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; -import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.ObjectIterator; import java.io.IOException; import java.io.UncheckedIOException; @@ -39,15 +35,15 @@ import org.bukkit.craftbukkit.event.CraftEventFactory; public class PersistentEntitySectionManager implements AutoCloseable { static final Logger LOGGER = LogUtils.getLogger(); - final Set knownUuids = Sets.newHashSet(); + final Set knownUuids = Sets.newConcurrentHashSet(); final LevelCallback callbacks; public final EntityPersistentStorage permanentStorage; private final EntityLookup visibleEntityStorage = new EntityLookup<>(); final EntitySectionStorage sectionStorage; private final LevelEntityGetter entityGetter; - private final Long2ObjectMap chunkVisibility = new Long2ObjectOpenHashMap(); - private final Long2ObjectMap chunkLoadStatuses = new Long2ObjectOpenHashMap(); - private final LongSet chunksToUnload = new LongOpenHashSet(); + private final Long2ObjectMap chunkVisibility = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); + private final Long2ObjectMap chunkLoadStatuses = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); + private final LongSet chunksToUnload = LongSets.synchronize(new LongOpenHashSet()); private final Queue> loadingInbox = Queues.newConcurrentLinkedQueue(); public PersistentEntitySectionManager(Class entityClass, LevelCallback handler, EntityPersistentStorage dataAccess) { diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java index 878a42695ecedf0c3f2e6310e3ce44c6b6c36858..b61ed4d03848f86ca5e93b0374bbf4ca05369ad2 100644 --- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java @@ -2,18 +2,21 @@ package net.minecraft.world.level.gameevent; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.Set; + +import java.util.*; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ObjectLists; +import net.himeki.mcmtfabric.parallelised.ConcurrentDoublyLinkedList; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.phys.Vec3; public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry { - private final List listeners = Lists.newArrayList(); - private final Set listenersToRemove = Sets.newHashSet(); - private final List listenersToAdd = Lists.newArrayList(); + private final List listeners = new ConcurrentDoublyLinkedList<>(); + private final Set listenersToRemove = Sets.newConcurrentHashSet(); + private final List listenersToAdd = Lists.newCopyOnWriteArrayList(); private boolean processing; private final ServerLevel level; diff --git a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java index daa03360dd7044f10b20f36023b305dc7e0bb7df..35de9e9a9d211b16a8b945bc512c128709ec6bfc 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java +++ b/src/main/java/net/minecraft/world/level/levelgen/LegacyRandomSource.java @@ -19,17 +19,17 @@ public class LegacyRandomSource implements BitRandomSource { } @Override - public RandomSource fork() { + public synchronized RandomSource fork() { return new LegacyRandomSource(this.nextLong()); } @Override - public PositionalRandomFactory forkPositional() { + public synchronized PositionalRandomFactory forkPositional() { return new LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong()); } @Override - public void setSeed(long seed) { + public synchronized void setSeed(long seed) { if (!this.seed.compareAndSet(this.seed.get(), (seed ^ 25214903917L) & 281474976710655L)) { throw ThreadingDetector.makeThreadingException("LegacyRandomSource", (Thread)null); } else { @@ -38,7 +38,7 @@ public class LegacyRandomSource implements BitRandomSource { } @Override - public int next(int bits) { + public synchronized int next(int bits) { long l = this.seed.get(); long m = l * 25214903917L + 11L & 281474976710655L; if (!this.seed.compareAndSet(l, m)) { @@ -49,7 +49,7 @@ public class LegacyRandomSource implements BitRandomSource { } @Override - public double nextGaussian() { + public synchronized double nextGaussian() { return this.gaussianSource.nextGaussian(); } @@ -61,21 +61,21 @@ public class LegacyRandomSource implements BitRandomSource { } @Override - public RandomSource at(int x, int y, int z) { + public synchronized RandomSource at(int x, int y, int z) { long l = Mth.getSeed(x, y, z); long m = l ^ this.seed; return new LegacyRandomSource(m); } @Override - public RandomSource fromHashOf(String seed) { + public synchronized RandomSource fromHashOf(String seed) { int i = seed.hashCode(); return new LegacyRandomSource((long)i ^ this.seed); } @VisibleForTesting @Override - public void parityConfigString(StringBuilder info) { + public synchronized void parityConfigString(StringBuilder info) { info.append("LegacyPositionalRandomFactory{").append(this.seed).append("}"); } } diff --git a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java index 3d5ce92c77bc107e2ec2f54dc849b99c3abf9718..88d78f77740ee436fedd5159f8bafe91c1eb5ec1 100644 --- a/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java +++ b/src/main/java/net/minecraft/world/level/lighting/DynamicGraphMinFixedPoint.java @@ -1,17 +1,16 @@ package net.minecraft.world.level.lighting; -import it.unimi.dsi.fastutil.longs.Long2ByteMap; -import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongList; +import it.unimi.dsi.fastutil.longs.*; + +import java.util.Deque; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.function.LongPredicate; import net.minecraft.util.Mth; public abstract class DynamicGraphMinFixedPoint { private static final int NO_COMPUTED_LEVEL = 255; private final int levelCount; - private final LongLinkedOpenHashSet[] queues; + private final Deque[] queues; private final Long2ByteMap computedLevels; private int firstQueuedLevel; private volatile boolean hasWork; @@ -21,17 +20,10 @@ public abstract class DynamicGraphMinFixedPoint { throw new IllegalArgumentException("Level count must be < 254."); } else { this.levelCount = levelCount; - this.queues = new LongLinkedOpenHashSet[levelCount]; + this.queues = new Deque[levelCount]; for(int i = 0; i < levelCount; ++i) { - this.queues[i] = new LongLinkedOpenHashSet(expectedLevelSize, 0.5F) { - protected void rehash(int i) { - if (i > expectedLevelSize) { - super.rehash(i); - } - - } - }; + this.queues[i] = new ConcurrentLinkedDeque(); } this.computedLevels = new Long2ByteOpenHashMap(expectedTotalSize, 0.5F) { @@ -191,8 +183,8 @@ public abstract class DynamicGraphMinFixedPoint { } else { while(this.firstQueuedLevel < this.levelCount && maxSteps > 0) { --maxSteps; - LongLinkedOpenHashSet longLinkedOpenHashSet = this.queues[this.firstQueuedLevel]; - long l = longLinkedOpenHashSet.removeFirstLong(); + Deque longLinkedOpenHashSet = this.queues[this.firstQueuedLevel]; + long l = longLinkedOpenHashSet.removeFirst(); int i = Mth.clamp(this.getLevel(l), 0, this.levelCount - 1); if (longLinkedOpenHashSet.isEmpty()) { this.checkFirstQueuedLevel(this.levelCount); diff --git a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java index 27b9cefc172b391824ead382a712b8b9b1ddfe45..4b65331a9192b7ac75141183493126ee730e697e 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/BinaryHeap.java @@ -4,9 +4,9 @@ public class BinaryHeap { private Node[] heap = new Node[128]; private int size; - public Node insert(Node node) { + public synchronized Node insert(Node node) { if (node.heapIdx >= 0) { - throw new IllegalStateException("OW KNOWS!"); + return node; } else { if (this.size == this.heap.length) { Node[] nodes = new Node[this.size << 1]; @@ -21,15 +21,15 @@ public class BinaryHeap { } } - public void clear() { + public synchronized void clear() { this.size = 0; } - public Node peek() { + public synchronized Node peek() { return this.heap[0]; } - public Node pop() { + public synchronized Node pop() { Node node = this.heap[0]; this.heap[0] = this.heap[--this.size]; this.heap[this.size] = null; @@ -41,7 +41,7 @@ public class BinaryHeap { return node; } - public void remove(Node node) { + public synchronized void remove(Node node) { this.heap[node.heapIdx] = this.heap[--this.size]; this.heap[this.size] = null; if (this.size > node.heapIdx) { @@ -55,7 +55,7 @@ public class BinaryHeap { node.heapIdx = -1; } - public void changeCost(Node node, float weight) { + public synchronized void changeCost(Node node, float weight) { float f = node.f; node.f = weight; if (weight < f) { @@ -66,11 +66,14 @@ public class BinaryHeap { } - public int size() { + public synchronized int size() { return this.size; } private void upHeap(int index) { + if(index == -1){ + return; + } Node node = this.heap[index]; int i; @@ -90,6 +93,9 @@ public class BinaryHeap { } private void downHeap(int index) { + if(index == -1){ + return; + } Node node = this.heap[index]; float f = node.f; @@ -135,11 +141,11 @@ public class BinaryHeap { node.heapIdx = index; } - public boolean isEmpty() { + public synchronized boolean isEmpty() { return this.size == 0; } - public Node[] getHeap() { + public synchronized Node[] getHeap() { Node[] nodes = new Node[this.size()]; System.arraycopy(this.heap, 0, nodes, 0, this.size()); return nodes; diff --git a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java index b0bae04ab5a93dd4cf1eeeb02bed1e508e1f2913..d427735eff0056c171591709829d0bb76f7bb6f3 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/FlyNodeEvaluator.java @@ -1,6 +1,7 @@ package net.minecraft.world.level.pathfinder; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.EnumSet; import java.util.List; @@ -15,7 +16,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; public class FlyNodeEvaluator extends WalkNodeEvaluator { - private final Long2ObjectMap pathTypeByPosCache = new Long2ObjectOpenHashMap<>(); + private final Long2ObjectMap pathTypeByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); private static final float SMALL_MOB_INFLATED_START_NODE_BOUNDING_BOX = 1.5F; private static final int MAX_START_NODE_CANDIDATES = 10; diff --git a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java index a8a2594b8f5b3ebf6a1f918c7d822ad35b051b17..c614bcfc2bbbbccc7c4aac9389d4780478e739d2 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/NodeEvaluator.java @@ -1,6 +1,7 @@ package net.minecraft.world.level.pathfinder; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; @@ -11,7 +12,7 @@ import net.minecraft.world.level.PathNavigationRegion; public abstract class NodeEvaluator { protected PathNavigationRegion level; protected Mob mob; - protected final Int2ObjectMap nodes = new Int2ObjectOpenHashMap<>(); + protected final Int2ObjectMap nodes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); protected int entityWidth; protected int entityHeight; protected int entityDepth; 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 a8af51a25b0f99c3a64d9150fdfcd6b818aa7581..cd2592552339a79361d2a4e7936731330e15f6fa 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java @@ -31,7 +31,7 @@ public class PathFinder { } @Nullable - public Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { + public synchronized Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { this.openSet.clear(); this.nodeEvaluator.prepare(world, mob); Node node = this.nodeEvaluator.getStart(); diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java index 6084631b5b502279b84f190dc62fc76b770e368e..f526adbd31e65fc74af48f6137d293a7a7ceafbb 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java @@ -2,6 +2,7 @@ package net.minecraft.world.level.pathfinder; import com.google.common.collect.Maps; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.Map; import javax.annotation.Nullable; @@ -17,7 +18,7 @@ import net.minecraft.world.level.material.FluidState; public class SwimNodeEvaluator extends NodeEvaluator { private final boolean allowBreaching; - private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); + private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); public SwimNodeEvaluator(boolean canJumpOutOfWater) { this.allowBreaching = canJumpOutOfWater; diff --git a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java index 894881018c659d874f28f5744f0b8247cfecb1c1..ae06f7ef9c4b8147508984f8b46176de46171285 100644 --- a/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java +++ b/src/main/java/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java @@ -1,8 +1,10 @@ package net.minecraft.world.level.pathfinder; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import it.unimi.dsi.fastutil.objects.Object2BooleanMaps; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import java.util.EnumSet; import javax.annotation.Nullable; @@ -33,8 +35,8 @@ public class WalkNodeEvaluator extends NodeEvaluator { public static final double SPACE_BETWEEN_WALL_POSTS = 0.5D; private static final double DEFAULT_MOB_JUMP_HEIGHT = 1.125D; protected float oldWaterCost; - private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); - private final Object2BooleanMap collisionCache = new Object2BooleanOpenHashMap<>(); + private final Long2ObjectMap pathTypesByPosCache = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); + private final Object2BooleanMap collisionCache = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>()); @Override public void prepare(PathNavigationRegion cachedWorld, Mob entity) { diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java index b1c594dc6a6b8a6c737b99272acab9e7dbd0ed63..a1ff442357dfea868c319fd3c10ae28e6fb81956 100644 --- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java +++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java @@ -3,7 +3,11 @@ package net.minecraft.world.level.redstone; import com.mojang.logging.LogUtils; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.locks.StampedLock; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -16,8 +20,8 @@ public class CollectingNeighborUpdater implements NeighborUpdater { private static final Logger LOGGER = LogUtils.getLogger(); private final Level level; private final int maxChainedNeighborUpdates; - private final ArrayDeque stack = new ArrayDeque<>(); - private final List addedThisLayer = new ArrayList<>(); + private final Deque stack = new ConcurrentLinkedDeque<>(); + private final List addedThisLayer = new CopyOnWriteArrayList<>(); private int count = 0; public CollectingNeighborUpdater(Level world, int maxChainDepth) { @@ -26,22 +30,22 @@ public class CollectingNeighborUpdater implements NeighborUpdater { } @Override - public void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) { + public synchronized void shapeUpdate(Direction direction, BlockState neighborState, BlockPos pos, BlockPos neighborPos, int flags, int maxUpdateDepth) { this.addAndRun(pos, new CollectingNeighborUpdater.ShapeUpdate(direction, neighborState, pos.immutable(), neighborPos.immutable(), flags)); } @Override - public void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) { + public synchronized void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos sourcePos) { this.addAndRun(pos, new CollectingNeighborUpdater.SimpleNeighborUpdate(pos, sourceBlock, sourcePos.immutable())); } @Override - public void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { + public synchronized void neighborChanged(BlockState state, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) { this.addAndRun(pos, new CollectingNeighborUpdater.FullNeighborUpdate(state, pos.immutable(), sourceBlock, sourcePos.immutable(), notify)); } @Override - public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) { + public synchronized void updateNeighborsAtExceptFromFacing(BlockPos pos, Block sourceBlock, @Nullable Direction except) { this.addAndRun(pos, new CollectingNeighborUpdater.MultiNeighborUpdate(pos.immutable(), sourceBlock, except)); } @@ -62,20 +66,18 @@ public class CollectingNeighborUpdater implements NeighborUpdater { if (!bl) { this.runUpdates(); } - } private void runUpdates() { try { - while(!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { - for(int i = this.addedThisLayer.size() - 1; i >= 0; --i) { + while (!this.stack.isEmpty() || !this.addedThisLayer.isEmpty()) { + for (int i = this.addedThisLayer.size() - 1; i >= 0; --i) { this.stack.push(this.addedThisLayer.get(i)); } - this.addedThisLayer.clear(); CollectingNeighborUpdater.NeighborUpdates neighborUpdates = this.stack.peek(); - while(this.addedThisLayer.isEmpty()) { + while (this.addedThisLayer.isEmpty()) { if (!neighborUpdates.runNext(this.level)) { this.stack.pop(); break; @@ -87,7 +89,6 @@ public class CollectingNeighborUpdater implements NeighborUpdater { this.addedThisLayer.clear(); this.count = 0; } - } static record FullNeighborUpdate(BlockState state, BlockPos pos, Block block, BlockPos neighborPos, boolean movedByPiston) implements CollectingNeighborUpdater.NeighborUpdates { diff --git a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java index ac807277a6b26d140ea9873d17c7aa4fb5fe37b2..4c75f50ab0184637b72e08936ff8808ad6c6fb5f 100644 --- a/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java +++ b/src/main/java/net/minecraft/world/ticks/LevelChunkTicks.java @@ -13,6 +13,8 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.Nullable; + +import it.unimi.dsi.fastutil.objects.ObjectSets; import net.minecraft.core.BlockPos; import net.minecraft.nbt.ListTag; import net.minecraft.world.level.ChunkPos; @@ -21,7 +23,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon private final Queue> tickQueue = new PriorityQueue<>(ScheduledTick.DRAIN_ORDER); @Nullable private List> pendingTicks; - private final Set> ticksPerPosition = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); + private final Set> ticksPerPosition = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); @Nullable private BiConsumer, ScheduledTick> onTickAdded; @@ -29,11 +31,11 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon private boolean dirty; private long lastSaved = Long.MIN_VALUE; - public boolean isDirty(final long tick) { + public synchronized boolean isDirty(final long tick) { return this.dirty || (!this.tickQueue.isEmpty() && tick != this.lastSaved); } - public void clearDirty() { + public synchronized void clearDirty() { this.dirty = false; } // Paper end - add dirty flag @@ -50,17 +52,17 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon } - public void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> tickConsumer) { + public synchronized void setOnTickAdded(@Nullable BiConsumer, ScheduledTick> tickConsumer) { this.onTickAdded = tickConsumer; } @Nullable - public ScheduledTick peek() { + public synchronized ScheduledTick peek() { return this.tickQueue.peek(); } @Nullable - public ScheduledTick poll() { + public synchronized ScheduledTick poll() { ScheduledTick scheduledTick = this.tickQueue.poll(); if (scheduledTick != null) { this.dirty = true; // Paper - add dirty flag @@ -71,7 +73,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon } @Override - public void schedule(ScheduledTick orderedTick) { + public synchronized void schedule(ScheduledTick orderedTick) { if (this.ticksPerPosition.add(orderedTick)) { this.dirty = true; // Paper - add dirty flag this.scheduleUnchecked(orderedTick); @@ -88,11 +90,11 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon } @Override - public boolean hasScheduledTick(BlockPos pos, T type) { + public synchronized boolean hasScheduledTick(BlockPos pos, T type) { return this.ticksPerPosition.contains(ScheduledTick.probe(type, pos)); } - public void removeIf(Predicate> predicate) { + public synchronized void removeIf(Predicate> predicate) { Iterator> iterator = this.tickQueue.iterator(); while(iterator.hasNext()) { @@ -105,17 +107,17 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon } - public Stream> getAll() { + public synchronized Stream> getAll() { return this.tickQueue.stream(); } @Override - public int count() { + public synchronized int count() { return this.tickQueue.size() + (this.pendingTicks != null ? this.pendingTicks.size() : 0); } @Override - public ListTag save(long l, Function function) { + public synchronized ListTag save(long l, Function function) { this.lastSaved = l; // Paper - add dirty system to level ticks ListTag listTag = new ListTag(); if (this.pendingTicks != null) { @@ -131,7 +133,7 @@ public class LevelChunkTicks implements SerializableTickContainer, TickCon return listTag; } - public void unpack(long time) { + public synchronized void unpack(long time) { if (this.pendingTicks != null) { // Paper start - add dirty system to level chunk ticks if (this.tickQueue.isEmpty()) { diff --git a/src/main/java/net/minecraft/world/ticks/LevelTicks.java b/src/main/java/net/minecraft/world/ticks/LevelTicks.java index 5dea8414964e0d2d1fb15a6baa27227e9722bfc7..2203adc2a68e7fb253e353098fd6ddad521e3a32 100644 --- a/src/main/java/net/minecraft/world/ticks/LevelTicks.java +++ b/src/main/java/net/minecraft/world/ticks/LevelTicks.java @@ -1,24 +1,9 @@ package net.minecraft.world.ticks; -import it.unimi.dsi.fastutil.longs.Long2LongMap; -import it.unimi.dsi.fastutil.longs.Long2LongMaps; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.*; import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.LongSummaryStatistics; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.LongPredicate; -import java.util.function.Predicate; -import java.util.function.Supplier; +import it.unimi.dsi.fastutil.objects.ObjectSets; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -26,31 +11,36 @@ import net.minecraft.core.Vec3i; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.levelgen.structure.BoundingBox; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.BiConsumer; +import java.util.function.LongPredicate; +import java.util.function.Predicate; +import java.util.function.Supplier; public class LevelTicks implements LevelTickAccess { private static final Comparator> CONTAINER_DRAIN_ORDER = (a, b) -> { return ScheduledTick.INTRA_TICK_DRAIN_ORDER.compare(a.peek(), b.peek()); }; private final LongPredicate tickCheck; - private final Supplier profiler; - private final Long2ObjectMap> allContainers = new Long2ObjectOpenHashMap<>(); + private final Long2ObjectMap> allContainers = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); private final Long2LongMap nextTickForContainer = Util.make(new Long2LongOpenHashMap(), (map) -> { map.defaultReturnValue(Long.MAX_VALUE); }); private final Queue> containersToTick = new PriorityQueue<>(CONTAINER_DRAIN_ORDER); - private final Queue> toRunThisTick = new ArrayDeque<>(); - private final List> alreadyRunThisTick = new ArrayList<>(); - private final Set> toRunThisTickSet = new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH); + private final Queue> toRunThisTick = new ConcurrentLinkedDeque<>(); + private final List> alreadyRunThisTick = new CopyOnWriteArrayList<>(); + private final Set> toRunThisTickSet = ObjectSets.synchronize(new ObjectOpenCustomHashSet<>(ScheduledTick.UNIQUE_TICK_HASH)); + private final BiConsumer, ScheduledTick> chunkScheduleUpdater = (chunkTickScheduler, tick) -> { if (tick.equals(chunkTickScheduler.peek())) { this.updateContainerScheduling(tick); } - }; public LevelTicks(LongPredicate tickingFutureReadyPredicate, Supplier profilerGetter) { this.tickCheck = tickingFutureReadyPredicate; - this.profiler = profilerGetter; } public void addContainer(ChunkPos pos, LevelChunkTicks scheduler) { @@ -123,7 +113,9 @@ public class LevelTicks implements LevelTickAccess { entry.setValue(scheduledTick.triggerTick()); } else if (this.tickCheck.test(l)) { objectIterator.remove(); - this.containersToTick.add(levelChunkTicks); + synchronized (this.containersToTick){ + this.containersToTick.add(levelChunkTicks); + } } } } @@ -133,27 +125,29 @@ public class LevelTicks implements LevelTickAccess { private void drainContainers(long time, int maxTicks) { LevelChunkTicks levelChunkTicks; - while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { - ScheduledTick scheduledTick = levelChunkTicks.poll(); - this.scheduleForThisTick(scheduledTick); - this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); - ScheduledTick scheduledTick2 = levelChunkTicks.peek(); - if (scheduledTick2 != null) { - if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { - this.containersToTick.add(levelChunkTicks); - } else { - this.updateContainerScheduling(scheduledTick2); + synchronized (this.containersToTick){ + while(this.canScheduleMoreTicks(maxTicks) && (levelChunkTicks = this.containersToTick.poll()) != null) { + ScheduledTick scheduledTick = levelChunkTicks.poll(); + this.scheduleForThisTick(scheduledTick); + this.drainFromCurrentContainer(this.containersToTick, levelChunkTicks, time, maxTicks); + ScheduledTick scheduledTick2 = levelChunkTicks.peek(); + if (scheduledTick2 != null) { + if (scheduledTick2.triggerTick() <= time && this.canScheduleMoreTicks(maxTicks)) { + this.containersToTick.add(levelChunkTicks); + } else { + this.updateContainerScheduling(scheduledTick2); + } } } } - } private void rescheduleLeftoverContainers() { - for(LevelChunkTicks levelChunkTicks : this.containersToTick) { - this.updateContainerScheduling(levelChunkTicks.peek()); + synchronized (this.containersToTick){ + for(LevelChunkTicks levelChunkTicks : this.containersToTick) { + this.updateContainerScheduling(levelChunkTicks.peek()); + } } - } private void updateContainerScheduling(ScheduledTick tick) { @@ -201,7 +195,9 @@ public class LevelTicks implements LevelTickAccess { private void cleanupAfterTick() { this.toRunThisTick.clear(); - this.containersToTick.clear(); + synchronized (this.containersToTick){ + this.containersToTick.clear(); + } this.alreadyRunThisTick.clear(); this.toRunThisTickSet.clear(); }