Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static LiteralArgumentBuilder<CommandSourceStack> registerConfig(LiteralA
.then(buildReloadCommand())
.then(buildSynchronizedEntitiesCommand())
.then(buildAsyncEntitySpawnCommand())
.then(buildAsyncMobSpawningCommand())
.then(buildAsyncRandomTicksCommand())
);
}
Expand Down Expand Up @@ -155,6 +156,28 @@ private static LiteralArgumentBuilder<CommandSourceStack> buildAsyncRandomTicksC
);
}

private static LiteralArgumentBuilder<CommandSourceStack> buildAsyncMobSpawningCommand() {
return literal("setAsyncMobSpawning")
.executes(ctx -> {
sendMessage(ctx, "Current value of async natural mob spawning: ",
String.valueOf(AsyncConfig.enableAsyncMobSpawning),
false);
return 1;
})
.then(Commands.argument("value", BoolArgumentType.bool())
.executes(ctx -> {
boolean value = BoolArgumentType.getBool(ctx, "value");
AsyncConfig.enableAsyncMobSpawning = value;
PlatformUtils.saveConfig();

sendMessage(ctx, "Async Natural Mob Spawning set to ",
String.valueOf(value),
true);
return 1;
})
);
}

private static void displaySynchronizedEntities(CommandContext<CommandSourceStack> ctx) {
Set<String> entities = AsyncConfig.synchronizedEntities;
MutableComponent message = prefix.copy()
Expand Down Expand Up @@ -266,4 +289,4 @@ private static void sendErrorMessage(CommandContext<CommandSourceStack> ctx, Str
.append(Component.literal(suffix).withStyle(style -> style.withColor(ChatFormatting.RED)));
ctx.getSource().sendSuccess(() -> message, true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private static void showGeneralStats(CommandSourceStack source) {

boolean enabled = !AsyncConfig.disabled;
boolean asyncSpawn = AsyncConfig.enableAsyncSpawn;
boolean asyncMobSpawning = AsyncConfig.enableAsyncMobSpawning;
boolean asyncRandomTicks = AsyncConfig.enableAsyncRandomTicks;

MutableComponent message = prefix.copy()
Expand All @@ -73,6 +74,10 @@ private static void showGeneralStats(CommandSourceStack source) {
.append(Component.literal(asyncSpawn ? "Enabled" : "Disabled")
.withStyle(asyncSpawn ? ChatFormatting.GREEN : ChatFormatting.RED))

.append(Component.literal("\nAsync Mob Spawning: ").withStyle(ChatFormatting.WHITE))
.append(Component.literal(asyncMobSpawning ? "Enabled" : "Disabled")
.withStyle(asyncMobSpawning ? ChatFormatting.GREEN : ChatFormatting.RED))

.append(Component.literal("\nAsync Random Ticks: ").withStyle(ChatFormatting.WHITE))
.append(Component.literal(asyncRandomTicks ? "Enabled" : "Disabled")
.withStyle(asyncRandomTicks ? ChatFormatting.GREEN : ChatFormatting.RED))
Expand Down Expand Up @@ -163,4 +168,4 @@ private static void showEntityStats(CommandSourceStack source, int topCount) {
source.sendSuccess(() -> message, false);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class AsyncConfig {
public static boolean disabled = false;
public static int maxThreads = -1;
public static boolean enableAsyncSpawn = true;
public static boolean enableAsyncMobSpawning = getDefaultAsyncMobSpawning();
public static boolean enableAsyncRandomTicks = false;
public static Set<String> synchronizedEntities = getDefaultSynchronizedEntities();

Expand All @@ -34,6 +35,10 @@ public static Set<String> getDefaultSynchronizedEntities() {
return defaultSynchronizedEntities;
}

public static boolean getDefaultAsyncMobSpawning() {
return !PlatformUtils.isModLoaded("c2me");
}

public static int getParallelism() {
if (maxThreads <= 0) return Runtime.getRuntime().availableProcessors();
return Math.max(1, Math.min(Runtime.getRuntime().availableProcessors(), maxThreads));
Expand Down Expand Up @@ -121,4 +126,4 @@ public static void onConfigLoaded() {
public static void clearCaches() {
syncCache.clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ public abstract class ServerChunkCacheMixin extends ChunkSource {
@Unique
private final AtomicBoolean async$spawnCountsReady = new AtomicBoolean(false);

@Unique
private volatile boolean async$forceSyncMobSpawning = false;

@Shadow
public abstract void tickSpawningChunk(LevelChunk chunk, long timeInhabited, List<MobCategory> spawnCategories, NaturalSpawner.SpawnState spawnState);

Expand All @@ -96,6 +99,16 @@ public abstract class ServerChunkCacheMixin extends ChunkSource {
return;
}

if (async$isHighRiskAsyncChunkRequest(leastStatus, create)) {
String message = String.format(
Locale.ROOT,
"Blocked async chunk request that could synchronously load or generate chunks on thread %s at [%d, %d] status %s create=%s",
Thread.currentThread().getName(), x, z, leastStatus, create
);
ParallelProcessor.LOGGER.warn(message);
throw new IllegalStateException(message);
}

CompletableFuture<ChunkResult<ChunkAccess>> future = CompletableFuture.supplyAsync(
() -> this.getChunkFutureMainThread(x, z, leastStatus, create),
this.mainThreadProcessor
Expand All @@ -118,6 +131,11 @@ public abstract class ServerChunkCacheMixin extends ChunkSource {
cir.setReturnValue(chunk);
}

@Unique
private boolean async$isHighRiskAsyncChunkRequest(ChunkStatus leastStatus, boolean create) {
return create || leastStatus.isOrAfter(ChunkStatus.STRUCTURE_STARTS);
}

@Unique
private @Nullable ChunkAccess async$tryGetChunk(int x, int z, ChunkStatus leastStatus) {
ChunkHolder holder = this.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
Expand All @@ -139,18 +157,25 @@ private void shortcutGetChunkNow(int chunkX, int chunkZ, CallbackInfoReturnable<
if (Thread.currentThread() != this.mainThread) {
final ChunkHolder holder = this.getVisibleChunkIfPresent(ChunkPos.asLong(chunkX, chunkZ));
if (holder != null) {
final CompletableFuture<ChunkResult<ChunkAccess>> future = holder.scheduleChunkGenerationTask(ChunkStatus.FULL, this.chunkMap);
ChunkAccess chunk = future.getNow(ChunkHolder.UNLOADED_CHUNK).orElse(null);
ChunkAccess chunk = holder.getChunkIfPresent(ChunkStatus.FULL);
if (chunk instanceof ImposterProtoChunk imposter) {
chunk = imposter.getWrapped();
}
if (chunk instanceof LevelChunk worldChunk) {
cir.setReturnValue(worldChunk);
}
}
}
}

@Unique
private boolean async$canUseAsyncMobSpawning() {
return !AsyncConfig.disabled && AsyncConfig.enableAsyncSpawn && AsyncConfig.enableAsyncMobSpawning && !async$forceSyncMobSpawning;
}

@Inject(method = "tickChunks()V", at = @At("TAIL"))
private void tickChunks(CallbackInfo ci) {
if (AsyncConfig.disabled || !AsyncConfig.enableAsyncSpawn) return;
if (!async$canUseAsyncMobSpawning()) return;

if (async$firstRunSpawnCounts) {
async$firstRunSpawnCounts = false;
Expand All @@ -159,8 +184,14 @@ private void tickChunks(CallbackInfo ci) {
if (async$spawnCountsReady.getAndSet(false)) {
final int i = distanceManager.getNaturalSpawnChunkCount();
ParallelProcessor.tickPool.submit(() -> {
lastSpawnState = NaturalSpawner.createState(i, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
async$spawnCountsReady.set(true);
try {
lastSpawnState = NaturalSpawner.createState(i, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
} catch (Throwable throwable) {
async$forceSyncMobSpawning = true;
ParallelProcessor.LOGGER.warn("Async natural spawn state calculation failed, disabling async natural mob spawning for this level", throwable);
} finally {
async$spawnCountsReady.set(true);
}
});
}
}
Expand All @@ -170,7 +201,7 @@ private void tickChunksSpawn(ProfilerFiller profiler, long timeInhabited, Operat
profiler.push("naturalSpawnCount");
int i = this.distanceManager.getNaturalSpawnChunkCount();

if (AsyncConfig.disabled || !AsyncConfig.enableAsyncSpawn || async$firstRunSpawnCounts) {
if (!async$canUseAsyncMobSpawning() || async$firstRunSpawnCounts) {
lastSpawnState = NaturalSpawner.createState(i, this.level.getAllEntities(), this::getFullChunk, new LocalMobCapCalculator(this.chunkMap));
}

Expand All @@ -186,7 +217,7 @@ private void tickChunksSpawn(ProfilerFiller profiler, long timeInhabited, Operat

profiler.popPush("tickSpawningChunks");

if (!AsyncConfig.disabled && AsyncConfig.enableAsyncSpawn) {
if (async$canUseAsyncMobSpawning()) {
NaturalSpawner.SpawnState currentState = lastSpawnState;
if (currentState != null) {
CompletableFuture.runAsync(() -> {
Expand All @@ -199,21 +230,8 @@ private void tickChunksSpawn(ProfilerFiller profiler, long timeInhabited, Operat
}
}
}, ParallelProcessor.tickPool).exceptionally(e -> {
ParallelProcessor.LOGGER.error("Error in async entity spawning, switching to synchronous", e);
List<LevelChunk> list1 = this.spawningChunks;
try {
profiler.popPush("filteringSpawningChunks");
this.chunkMap.collectSpawningChunks(list1);
profiler.popPush("shuffleSpawningChunks");
Util.shuffle(list1, this.level.random);
profiler.popPush("tickSpawningChunks");

for(LevelChunk levelchunk : list1) {
this.tickSpawningChunk(levelchunk, timeInhabited, list, lastSpawnState);
}
} finally {
list1.clear();
}
async$forceSyncMobSpawning = true;
ParallelProcessor.LOGGER.error("Error in async natural mob spawning, disabling async natural mob spawning for this level", e);
return null;
});
}
Expand Down Expand Up @@ -242,4 +260,4 @@ private void tickChunksSpawn(ProfilerFiller profiler, long timeInhabited, Operat
}
profiler.pop();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class AsyncConfig {
"maxThreads",
"synchronizedEntities",
"enableAsyncSpawn",
"enableAsyncMobSpawning",
"enableAsyncRandomTicks"
);

Expand Down Expand Up @@ -63,6 +64,8 @@ List of entity IDs or namespaces (*):
- 'minecraft:*' = all entities in namespace""");
setWithComment("enableAsyncSpawn", enableAsyncSpawn,
"Enables async entity spawning. WARNING: incompatible with Carpet's lagFreeSpawning.");
setWithComment("enableAsyncMobSpawning", enableAsyncMobSpawning,
"Enables async natural mob spawning. Defaults to false when C2ME is detected to avoid chunk-load deadlocks.");
setWithComment("enableAsyncRandomTicks", enableAsyncRandomTicks,
"Experimental! Enables async random ticks.");

Expand All @@ -81,6 +84,7 @@ private static void loadConfigValues() {
disabled = CONFIG.getOrElse("disabled", disabled);
maxThreads = CONFIG.getOrElse("maxThreads", maxThreads);
enableAsyncSpawn = CONFIG.getOrElse("enableAsyncSpawn", enableAsyncSpawn);
enableAsyncMobSpawning = CONFIG.getOrElse("enableAsyncMobSpawning", enableAsyncMobSpawning);
enableAsyncRandomTicks = CONFIG.getOrElse("enableAsyncRandomTicks", enableAsyncRandomTicks);

List<String> entries = CONFIG.get("synchronizedEntities");
Expand All @@ -100,6 +104,7 @@ List of entity IDs or namespaces (*):
- 'minecraft:zombie' = specific entity
- 'minecraft:*' = all entities in namespace""");
setCommentIfExists("enableAsyncSpawn", "Enables async entity spawning. WARNING: incompatible with Carpet's lagFreeSpawning.");
setCommentIfExists("enableAsyncMobSpawning", "Enables async natural mob spawning. Defaults to false when C2ME is detected to avoid chunk-load deadlocks.");
setCommentIfExists("enableAsyncRandomTicks", "Experimental! Enables async random ticks.");
}

Expand Down Expand Up @@ -129,7 +134,8 @@ private static void setDefaultValues() {
disabled = false;
maxThreads = -1;
enableAsyncSpawn = true;
enableAsyncMobSpawning = getDefaultAsyncMobSpawning();
enableAsyncRandomTicks = false;
synchronizedEntities = getDefaultSynchronizedEntities();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class AsyncConfig {
private static final ModConfigSpec.IntValue maxThreads;
private static final ModConfigSpec.ConfigValue<List<? extends String>> synchronizedEntities;
private static final ModConfigSpec.BooleanValue enableAsyncSpawn;
private static final ModConfigSpec.BooleanValue enableAsyncMobSpawning;
private static final ModConfigSpec.BooleanValue enableAsyncRandomTicks;

static {
Expand All @@ -42,6 +43,9 @@ List of entity IDs or namespaces (*):
enableAsyncSpawn = BUILDER.comment("Enables async entity spawning. WARNING: incompatible with Carpet's lagFreeSpawning.")
.define("enableAsyncSpawn", com.axalotl.async.common.config.AsyncConfig.enableAsyncSpawn);

enableAsyncMobSpawning = BUILDER.comment("Enables async natural mob spawning. Defaults to false when C2ME is detected to avoid chunk-load deadlocks.")
.define("enableAsyncMobSpawning", com.axalotl.async.common.config.AsyncConfig.enableAsyncMobSpawning);

enableAsyncRandomTicks = BUILDER.comment("Experimental! Enables async random ticks.")
.define("enableAsyncRandomTicks", com.axalotl.async.common.config.AsyncConfig.enableAsyncRandomTicks);

Expand All @@ -54,6 +58,7 @@ public static void loadConfig() {
com.axalotl.async.common.config.AsyncConfig.disabled = disabled.get();
com.axalotl.async.common.config.AsyncConfig.maxThreads = maxThreads.get();
com.axalotl.async.common.config.AsyncConfig.enableAsyncSpawn = enableAsyncSpawn.get();
com.axalotl.async.common.config.AsyncConfig.enableAsyncMobSpawning = enableAsyncMobSpawning.get();
com.axalotl.async.common.config.AsyncConfig.enableAsyncRandomTicks = enableAsyncRandomTicks.get();

List<? extends String> entries = synchronizedEntities.get();
Expand All @@ -71,9 +76,10 @@ public static void saveConfig() {
disabled.set(com.axalotl.async.common.config.AsyncConfig.disabled);
maxThreads.set(com.axalotl.async.common.config.AsyncConfig.maxThreads);
enableAsyncSpawn.set(com.axalotl.async.common.config.AsyncConfig.enableAsyncSpawn);
enableAsyncMobSpawning.set(com.axalotl.async.common.config.AsyncConfig.enableAsyncMobSpawning);
enableAsyncRandomTicks.set(com.axalotl.async.common.config.AsyncConfig.enableAsyncRandomTicks);
synchronizedEntities.set(new ArrayList<>(com.axalotl.async.common.config.AsyncConfig.synchronizedEntities));
SPEC.save();
com.axalotl.async.common.config.AsyncConfig.onConfigLoaded();
}
}
}