Compare commits

...

18 Commits

Author SHA1 Message Date
Spottedleaf
a33ac1dc01 Implement PlatformHooks#addTicketForEnderPearls
Allows implementing Paper's legacy ender pearl behavior config
option.
2025-06-20 20:24:15 -07:00
Spottedleaf
da21aeca85 Set version to 0.4.0-SNAPSHOT 2025-06-20 19:21:54 -07:00
Spottedleaf
694dab69c0 Set version to 0.4.0-beta.2 2025-06-20 18:55:15 -07:00
Spottedleaf
0764327cba Do not allow ticket level decreases to be processed asynchronously
Note: This cannot happen on the Fabric/NeoForge versions since
async ticket level processing is not allowed, but can happen on
Paper. This change is made here so that Paper can
remain in sync.

Ticket level decreases may be handled asynchronously when the
off-thread invokes processTicketUpdates() when the main thread
is running ChunkHolderManager#tick(). This is because the ticket
update is queued during tick(), but not executed (invoking
processTicketUpdates) until after releasing the ticket lock.
This creates a small window for an off-thread to invoke
processTicketUpdates() and steal the update.

When the update is stolen, the full chunk status update (if any)
will be eventually queued to execute via the chunk task queue.
If the chunk queue is processed during the server tick at any
point other than the ChunkHolderManager tick, then any ticket
level decrease will violate an important invariant in the
Moonrise chunk system: ticket level decreases only occur during
ChunkHolderManager tick. This invariant exists to make interfacing
with the chunk system easier, especially working with off-thread
contexts.

This change is specifically made to work towards fixing
https://github.com/PaperMC/Folia/issues/363
2025-06-20 18:53:29 -07:00
Spottedleaf
724b8ef20a Fix incompatibility with "Fast IP Ping"
Inject in a different place to prevent the mixin from failing to
apply. It looks like we do essentially the same logic anyways.
2025-06-20 18:52:50 -07:00
Spottedleaf
0ea5e4dcf5 Add chunk unload delay config option
The default config option is no unload delay, so that
it maintains parity with Vanilla.

Fixes https://github.com/Tuinity/Moonrise/issues/115
2025-06-20 14:03:28 -07:00
Spottedleaf
1f8e863c4b Fix MC-297591
Vanilla does not increment ticket timeouts if the chunk is
progressing in generation. They made this change in 1.21.6
so that the ender pearl ticket does not expire if the chunk
fails to generate before the timeout expires. Rather than
blindly adjusting the entire system behavior to fix this
small issue, we instead add non-expirable tickets to keep
ender pearls ticking.
2025-06-20 13:00:53 -07:00
Spottedleaf
9d58071cf5 Set version to 0.4.0-SNAPSHOT 2025-06-18 11:12:12 -07:00
Spottedleaf
303d763167 Set version to 0.4.0-beta.1 2025-06-18 11:08:41 -07:00
Spottedleaf
9e3a55f7bc Update cloth config api, re-enable GUI 2025-06-18 10:31:09 -07:00
Spottedleaf
aef2b81d6e Implement WaypointTransmitter#isChunkVisible
The Vanilla chunk tracker on Moonrise always returns false. We need
to redirect to Moonrise's chunk loader.
2025-06-17 17:42:53 -07:00
Spottedleaf
0451444abf Update to 1.21.6
We are awaiting cloth config for a release.

We also need to make a fix for MC-297591, as we revert Mojang's solution.
Mojang's solution makes the rest of the chunk system's timing with
tickets unpredictable, which is almost certainly too large of a change
on its own. Surely, the ender pearl code could be modified to fix
this properly.
2025-06-17 09:33:11 -07:00
Spottedleaf
9a4078966d Fix infinite loop in RegionFile IO
If an exception is thrown during decompress then the read process
would be started again, which of course would eventually throw in
the decompress process.
2025-06-09 02:28:34 -07:00
Jason Penilla
b2a7a92c74 - Uncomment neoforge config screen, it doesn't crash unless you try to use it
- Don't register gui source set when gui is disabled
2025-06-03 13:50:58 -07:00
Jason Penilla
30b011246c Allow starting fabric mod without ModMenu/Cloth working 2025-06-03 13:41:33 -07:00
Jason Penilla
91455be558 Downgrade MDG
Something broke with ATs
2025-06-03 13:35:20 -07:00
Jason Penilla
41ccbb2611 Update Gradle & plugins, use NeoForge PR repo 2025-06-03 13:12:39 -07:00
Spottedleaf
b312be2921 Preliminary work to update to 1.16.2-pre1
I don't see many changes to areas we affect. Waiting on
modmenu/cloth to update as well for more testing.

HappyGhast is a new hard colliding entity.
2025-06-03 11:54:35 -07:00
36 changed files with 336 additions and 111 deletions

View File

@@ -6,6 +6,12 @@ plugins {
id 'maven-publish' id 'maven-publish'
} }
boolean gui = enable_gui == "true"
if (gui) {
sourceSets.create("gui")
loom.createRemapConfigurations(sourceSets.gui)
}
dependencies { dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings() mappings loom.officialMojangMappings()
@@ -20,9 +26,16 @@ dependencies {
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) } libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}" if (gui) {
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}" guiCompileOnly(project(":"))
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}" runtimeOnly(sourceSets.gui.output)
shadow(sourceSets.gui.output)
modGuiImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
modRuntimeOnly "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
modGuiImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
modRuntimeOnly "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
}
modImplementation platform(fabricApiLibs.bom) modImplementation platform(fabricApiLibs.bom)
modImplementation fabricApiLibs.command.api.v2 modImplementation fabricApiLibs.command.api.v2
@@ -31,6 +44,14 @@ dependencies {
include fabricApiLibs.base include fabricApiLibs.base
} }
if (gui) {
afterEvaluate {
configurations.guiCompileOnly {
extendsFrom configurations.getByName("minecraftNamedCompile")
}
}
}
tasks.processResources { tasks.processResources {
def properties = [ def properties = [
"version": project.version, "version": project.version,

View File

@@ -6,6 +6,7 @@ import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType; import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import com.mojang.datafixers.DSL; import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Dynamic; import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrays; import it.unimi.dsi.fastutil.longs.LongArrays;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
@@ -18,6 +19,7 @@ import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
@@ -31,7 +33,9 @@ import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatusTasks; import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import org.slf4j.Logger;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -40,6 +44,8 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1"); private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
private static final Logger LOGGER = LogUtils.getLogger();
@Override @Override
public String getBrand() { public String getBrand() {
return "Moonrise"; return "Moonrise";
@@ -251,7 +257,9 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
@Override @Override
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) { public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities()); try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(chunk.problemPath(), LOGGER)) {
ChunkStatusTasks.postLoadProtoChunk(world, TagValueInput.create(scopedCollector, world.registryAccess(), chunk.getEntities()));
}
} }
@Override @Override
@@ -263,4 +271,9 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
public long[] getCounterTypesUncached(final TicketType type) { public long[] getCounterTypesUncached(final TicketType type) {
return type == TicketType.FORCED ? new long[] { ChunkSystemTicketType.COUNTER_TYPE_FORCED } : LongArrays.EMPTY_ARRAY; return type == TicketType.FORCED ? new long[] { ChunkSystemTicketType.COUNTER_TYPE_FORCED } : LongArrays.EMPTY_ARRAY;
} }
@Override
public boolean addTicketForEnderPearls(final ServerLevel world) {
return true;
}
} }

View File

@@ -36,7 +36,7 @@
"accessWidener": "moonrise.accesswidener", "accessWidener": "moonrise.accesswidener",
"depends": { "depends": {
"fabricloader": ">=${loader_version}", "fabricloader": ">=${loader_version}",
"minecraft": ">1.21.4 <1.21.6", "minecraft": ">1.21.5 <1.21.7",
"fabric-command-api-v2": "*" "fabric-command-api-v2": "*"
}, },
"custom": { "custom": {

View File

@@ -4,23 +4,25 @@ org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.configuration-cache=true org.gradle.configuration-cache=true
# Fabric Properties # Fabric Properties
# check these on https://modmuss50.me/fabric.html # check these on https://fabricmc.net/develop/
minecraft_version=1.21.5 minecraft_version=1.21.6
loader_version=0.16.14 loader_version=0.16.14
supported_minecraft_versions=1.21.5 supported_minecraft_versions=1.21.6
neoforge_version=21.5.65-beta neoforge_version=21.6.11-beta
neoform_version=1.21.5-20250325.162830 neoform_version=1.21.6-20250617.151856
fabric_api_version=0.123.0+1.21.5 fabric_api_version=0.127.1+1.21.6
snakeyaml_version=2.3 snakeyaml_version=2.3
concurrentutil_version=0.0.3 concurrentutil_version=0.0.3
yamlconfig_version=1.0.2 yamlconfig_version=1.0.2
cloth_version=18.0.145 cloth_version=19.0.147
modmenu_version=14.0.0-rc.2 modmenu_version=15.0.0-beta.2
# set to false when modmenu/cloth is not updated for the current minecraft version
enable_gui=true
junit_version=5.11.3 junit_version=5.11.3
# version ids from modrinth # version ids from modrinth
fabric_lithium_version=nhc57Td2 fabric_lithium_version=nhc57Td2
neo_lithium_version=P5VT33Jo neo_lithium_version=P5VT33Jo
# Mod Properties # Mod Properties
mod_version=0.3.0-SNAPSHOT mod_version=0.4.0-SNAPSHOT
maven_group=ca.spottedleaf.moonrise maven_group=ca.spottedleaf.moonrise
archives_base_name=moonrise archives_base_name=moonrise

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

6
gradlew vendored
View File

@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -205,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View File

@@ -7,6 +7,7 @@ import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType; import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import com.mojang.datafixers.DSL; import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Dynamic; import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@@ -17,6 +18,7 @@ import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@@ -30,6 +32,7 @@ import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatusTasks; import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.neoforged.neoforge.common.CommonHooks; import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForge;
@@ -38,12 +41,15 @@ import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.event.level.ChunkDataEvent; import net.neoforged.neoforge.event.level.ChunkDataEvent;
import net.neoforged.neoforge.event.level.ChunkEvent; import net.neoforged.neoforge.event.level.ChunkEvent;
import org.slf4j.Logger;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
public final class NeoForgeHooks extends BaseChunkSystemHooks implements PlatformHooks { public final class NeoForgeHooks extends BaseChunkSystemHooks implements PlatformHooks {
private static final Logger LOGGER = LogUtils.getLogger();
@Override @Override
public String getBrand() { public String getBrand() {
return "Moonrise"; return "Moonrise";
@@ -257,7 +263,9 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
@Override @Override
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) { public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities()); try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(chunk.problemPath(), LOGGER)) {
ChunkStatusTasks.postLoadProtoChunk(world, TagValueInput.create(scopedCollector, world.registryAccess(), chunk.getEntities()));
}
} }
@Override @Override
@@ -278,4 +286,9 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
return ret.toLongArray(); return ret.toLongArray();
} }
@Override
public boolean addTicketForEnderPearls(final ServerLevel world) {
return true;
}
} }

View File

@@ -28,7 +28,7 @@ side = "BOTH"
[[dependencies.moonrise]] [[dependencies.moonrise]]
modId = "minecraft" modId = "minecraft"
type = "required" type = "required"
versionRange = "(1.21.4,1.21.6)" versionRange = "(1.21.5,1.21.7)"
ordering = "NONE" ordering = "NONE"
side = "BOTH" side = "BOTH"

View File

@@ -22,7 +22,7 @@ pluginManagement {
} }
plugins { plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
id("quiet-fabric-loom") version "1.10.317" apply false id("quiet-fabric-loom") version "1.10.317" apply false
id("net.neoforged.moddev") version "2.0.80" apply false id("net.neoforged.moddev") version "2.0.80" apply false
id 'com.gradleup.shadow' version '8.3.6' apply false id 'com.gradleup.shadow' version '8.3.6' apply false

View File

@@ -102,6 +102,8 @@ public interface PlatformHooks extends ChunkSystemHooks {
public int modifyEntityTrackingRange(final Entity entity, final int currentRange); public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
public boolean addTicketForEnderPearls(final ServerLevel world);
public static final class Holder { public static final class Holder {
private Holder() { private Holder() {
} }

View File

@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.config.moonrise;
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig; import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon; import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.yamlconfig.InitialiseHook; import ca.spottedleaf.yamlconfig.InitialiseHook;
import ca.spottedleaf.yamlconfig.annotation.Adaptable; import ca.spottedleaf.yamlconfig.annotation.Adaptable;
@@ -38,7 +39,7 @@ public final class MoonriseConfig {
@Adaptable @Adaptable
public static final class Basic { public static final class Basic implements InitialiseHook {
@Serializable( @Serializable(
comment = """ comment = """
The maximum rate of chunks to send to any given player, per second. If this value is <= 0, The maximum rate of chunks to send to any given player, per second. If this value is <= 0,
@@ -72,6 +73,20 @@ public final class MoonriseConfig {
section = CHUNK_SYSTEM_SECTION section = CHUNK_SYSTEM_SECTION
) )
public double playerMaxGenRate = -1.0; public double playerMaxGenRate = -1.0;
@Serializable(
comment = """
The delay before chunks are unloaded around players once they leave their view distance.
The Vanilla value is 0 ticks. Setting this value higher (i.e 5s) will allow pets to teleport
to their owners when they teleport.
"""
)
public Duration playerChunkUnloadDelay = Duration.parse("0t");
@Override
public void initialise() {
RegionizedPlayerChunkLoader.setUnloadDelay(this.playerChunkUnloadDelay.getTimeTicks());
}
} }
@Serializable( @Serializable(

View File

@@ -16,7 +16,8 @@ public final class CoordinateUtils {
} }
public static long getChunkKey(final Entity entity) { public static long getChunkKey(final Entity entity) {
return ((Mth.lfloor(entity.getZ()) >> 4) << 32) | ((Mth.lfloor(entity.getX()) >> 4) & 0xFFFFFFFFL); final ChunkPos pos = entity.chunkPosition();
return ((long)pos.z << 32) | (pos.x & 0xFFFFFFFFL);
} }
public static long getChunkKey(final ChunkPos pos) { public static long getChunkKey(final ChunkPos pos) {

View File

@@ -607,6 +607,22 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
this.moonrise$getEntityLookup().addWorldGenChunkEntities(stream.toList(), null); // TODO this.moonrise$getEntityLookup().addWorldGenChunkEntities(stream.toList(), null); // TODO
} }
/**
* @reason Not needed in new chunk system, also avoid accessing old entity manager
* @author Spottedleaf
*/
@Redirect(
method = {
"method_72080",
"lambda$waitForChunkAndEntities$21"
},
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;processPendingLoads()V"
)
)
private void redirectWaitForChunks(final PersistentEntitySectionManager<Entity> instance) {}
/** /**
* @reason Level close now handles this * @reason Level close now handles this
* @author Spottedleaf * @author Spottedleaf

View File

@@ -15,8 +15,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayer.class) @Mixin(ServerPlayer.class)
abstract class ServerPlayerMixin extends Player implements ChunkSystemServerPlayer { abstract class ServerPlayerMixin extends Player implements ChunkSystemServerPlayer {
public ServerPlayerMixin(Level level, BlockPos blockPos, float f, GameProfile gameProfile) { public ServerPlayerMixin(final Level p_250508_, final GameProfile p_252153_) {
super(level, blockPos, f, gameProfile); super(p_250508_, p_252153_);
} }
@Unique @Unique

View File

@@ -29,6 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Predicate; import java.util.function.Predicate;
@Mixin(TicketStorage.class) @Mixin(TicketStorage.class)
@@ -193,8 +194,8 @@ abstract class TicketStorageMixin extends SavedData implements ChunkSystemTicket
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public void purgeStaleTickets() { public void purgeStaleTickets(final ChunkMap chunkMap) {
((ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager.tick(); ((ChunkSystemServerLevel)chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager.tick();
this.setDirty(); this.setDirty();
} }
@@ -208,11 +209,11 @@ abstract class TicketStorageMixin extends SavedData implements ChunkSystemTicket
method = "deactivateTicketsOnClosing", method = "deactivateTicketsOnClosing",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/world/level/TicketStorage;removeTicketIf(Ljava/util/function/Predicate;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V" target = "Lnet/minecraft/world/level/TicketStorage;removeTicketIf(Ljava/util/function/BiPredicate;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V"
) )
) )
private void avoidRemovingTicketsOnShutdown(final TicketStorage instance, private void avoidRemovingTicketsOnShutdown(final TicketStorage instance,
final Predicate<Ticket> predicate, final BiPredicate<Long, Ticket> predicate,
final Long2ObjectOpenHashMap<List<Ticket>> tickets) {} final Long2ObjectOpenHashMap<List<Ticket>> tickets) {}
/** /**
@@ -220,7 +221,7 @@ abstract class TicketStorageMixin extends SavedData implements ChunkSystemTicket
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public void removeTicketIf(final Predicate<Ticket> predicate, final Long2ObjectOpenHashMap<List<Ticket>> into) { public void removeTicketIf(final BiPredicate<Long, Ticket> predicate, final Long2ObjectOpenHashMap<List<Ticket>> into) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@@ -3,7 +3,10 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.PlatformHooks; import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType; import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -14,6 +17,11 @@ import java.util.concurrent.atomic.AtomicLong;
@Mixin(TicketType.class) @Mixin(TicketType.class)
abstract class TicketTypeMixin<T> implements ChunkSystemTicketType<T> { abstract class TicketTypeMixin<T> implements ChunkSystemTicketType<T> {
@Final
@Shadow
@Mutable
private long timeout;
@Unique @Unique
private static AtomicLong ID_GENERATOR; private static AtomicLong ID_GENERATOR;
@@ -66,4 +74,9 @@ abstract class TicketTypeMixin<T> implements ChunkSystemTicketType<T> {
return this.counterTypes = PlatformHooks.get().getCounterTypesUncached((TicketType)(Object)this); return this.counterTypes = PlatformHooks.get().getCounterTypesUncached((TicketType)(Object)this);
} }
@Override
public final void moonrise$setTimeout(final long to) {
this.timeout = to;
}
} }

View File

@@ -0,0 +1,25 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.waypoints.Waypoint;
import net.minecraft.world.waypoints.WaypointTransmitter;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@Mixin(WaypointTransmitter.class)
interface WaypointTransmitterMixin extends Waypoint {
/**
* @reason Redirect to the new player chunk loader, as the Vanilla chunk tracker is set to empty on Moonrise
* @author Spottedleaf
*/
@Overwrite
public static boolean isChunkVisible(final ChunkPos pos, final ServerPlayer player) {
final RegionizedPlayerChunkLoader.PlayerChunkLoaderData playerChunkLoader = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
return playerChunkLoader != null && playerChunkLoader.getSentChunksRaw().contains(CoordinateUtils.getChunkKey(pos));
}
}

View File

@@ -59,7 +59,7 @@ interface EntityGetterMixin {
continue; continue;
} }
if ((entity == null && otherEntity.canBeCollidedWith()) || (entity != null && entity.canCollideWith(otherEntity))) { if ((entity == null && otherEntity.canBeCollidedWith(entity)) || (entity != null && entity.canCollideWith(otherEntity))) {
ret.add(Shapes.create(otherEntity.getBoundingBox())); ret.add(Shapes.create(otherEntity.getBoundingBox()));
} }
} }

View File

@@ -1,10 +1,13 @@
package ca.spottedleaf.moonrise.mixin.serverlist; package ca.spottedleaf.moonrise.mixin.serverlist;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.multiplayer.resolver.ServerAddress;
import net.minecraft.client.multiplayer.resolver.ServerAddressResolver; import net.minecraft.client.multiplayer.resolver.ServerAddressResolver;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
@Mixin(ServerAddressResolver.class) @Mixin(ServerAddressResolver.class)
@@ -15,24 +18,23 @@ interface ServerAddressResolverMixin {
* @author Spottedleaf * @author Spottedleaf
*/ */
@Redirect( @Redirect(
method = { method = {
"method_36903", "method_36903",
"lambda$static$0" "lambda$static$0"
}, },
at = @At( at = @At(
value = "INVOKE", value = "NEW",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;" target = "(Ljava/net/InetAddress;I)Ljava/net/InetSocketAddress;"
) )
) )
private static InetAddress eliminateRDNS(final String name) throws UnknownHostException { private static InetSocketAddress eliminateRDNS(InetAddress addr, final int port,
final InetAddress ret = InetAddress.getByName(name); @Local(ordinal = 0, argsOnly = true) final ServerAddress serverAddress) throws UnknownHostException {
final byte[] address = addr.getAddress();
final byte[] address = ret.getAddress();
if (address != null) { if (address != null) {
// pass name to prevent rDNS // pass name to prevent rDNS
return InetAddress.getByAddress(name, address); addr = InetAddress.getByAddress(serverAddress.getHost(), address);
} }
return ret; return new InetSocketAddress(addr, port);
} }
} }

View File

@@ -15,7 +15,7 @@ public final class ChunkSystemConverters {
private static final int DEFAULT_ENTITY_CHUNK_DATA_VERSION = -1; private static final int DEFAULT_ENTITY_CHUNK_DATA_VERSION = -1;
private static int getCurrentVersion() { private static int getCurrentVersion() {
return SharedConstants.getCurrentVersion().getDataVersion().getVersion(); return SharedConstants.getCurrentVersion().dataVersion().version();
} }
private static int getDataVersion(final CompoundTag data, final int dfl) { private static int getDataVersion(final CompoundTag data, final int dfl) {

View File

@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.patches.chunk_system.entity;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData; import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import net.minecraft.server.level.FullChunkStatus; import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.animal.HappyGhast;
import net.minecraft.world.entity.monster.Shulker; import net.minecraft.world.entity.monster.Shulker;
import net.minecraft.world.entity.vehicle.AbstractMinecart; import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.entity.vehicle.Boat;
@@ -13,7 +14,7 @@ public interface ChunkSystemEntity {
// for mods to override // for mods to override
public default boolean moonrise$isHardCollidingUncached() { public default boolean moonrise$isHardCollidingUncached() {
return this instanceof Boat || this instanceof AbstractMinecart || this instanceof Shulker || ((Entity)this).canBeCollidedWith(); return this instanceof Boat || this instanceof AbstractMinecart || this instanceof Shulker || this instanceof HappyGhast || ((Entity)this).canBeCollidedWith(null);
} }
public FullChunkStatus moonrise$getChunkStatus(); public FullChunkStatus moonrise$getChunkStatus();

View File

@@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO {
LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr); LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr);
} }
if (compoundTag == null) { if (throwable == null && compoundTag == null) {
// need to re-try from the start // need to re-try from the start
this.scheduleReadIO(); this.scheduleReadIO();
return; return;

View File

@@ -5,6 +5,7 @@ import ca.spottedleaf.moonrise.common.list.EntityList;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData; import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity; import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@@ -14,6 +15,7 @@ import net.minecraft.nbt.Tag;
import net.minecraft.server.level.FullChunkStatus; import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
@@ -21,9 +23,14 @@ import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.storage.EntityStorage; import net.minecraft.world.level.chunk.storage.EntityStorage;
import net.minecraft.world.level.entity.Visibility; import net.minecraft.world.level.entity.Visibility;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import org.slf4j.Logger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
@@ -32,6 +39,8 @@ import java.util.function.Predicate;
public final class ChunkEntitySlices { public final class ChunkEntitySlices {
private static final Logger LOGGER = LogUtils.getLogger();
public final int minSection; public final int minSection;
public final int maxSection; public final int maxSection;
public final int chunkX; public final int chunkX;
@@ -74,9 +83,12 @@ public final class ChunkEntitySlices {
this.chunkData = chunkData; this.chunkData = chunkData;
} }
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) { public static List<Entity> readEntities(final ServerLevel world, final ChunkPos pos, final CompoundTag compoundTag) {
// TODO check this and below on update for format changes try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(pos), LOGGER)) {
return EntityType.loadEntitiesRecursive(compoundTag.getListOrEmpty("Entities"), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList()); ValueInput valueinput = TagValueInput.create(scopedCollector, world.registryAccess(), compoundTag);
// TODO check this and below on update for format changes
return EntityType.loadEntitiesRecursive(valueinput.childrenListOrEmpty("Entities"), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
}
} }
// Paper start - rewrite chunk system // Paper start - rewrite chunk system
@@ -104,12 +116,22 @@ public final class ChunkEntitySlices {
} }
final ListTag entitiesTag = new ListTag(); final ListTag entitiesTag = new ListTag();
for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) { try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(chunkPos), LOGGER)) {
CompoundTag compoundTag = new CompoundTag(); for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
if (entity.save(compoundTag)) { final TagValueOutput savedEntity = TagValueOutput.createWithContext(
entitiesTag.add(compoundTag); scopedCollector.forChild(entity.problemPath()), entity.registryAccess()
);
try {
if (entity.save(savedEntity)) {
entitiesTag.add(savedEntity.buildResult());
}
} catch (final Exception ex) {
LOGGER.error("Entity type " + entity.getType() + " failed to serialize", ex);
}
} }
} }
final CompoundTag ret = NbtUtils.addCurrentDataVersion(new CompoundTag()); final CompoundTag ret = NbtUtils.addCurrentDataVersion(new CompoundTag());
ret.put("Entities", entitiesTag); ret.put("Entities", entitiesTag);
ret.store("Position", ChunkPos.CODEC, chunkPos); ret.store("Position", ChunkPos.CODEC, chunkPos);

View File

@@ -45,8 +45,7 @@ public final class ClientEntityLookup extends EntityLookup {
final boolean ticking = this.tickingChunks.contains(CoordinateUtils.getChunkKey(chunkX, chunkZ)); final boolean ticking = this.tickingChunks.contains(CoordinateUtils.getChunkKey(chunkX, chunkZ));
final ChunkEntitySlices ret = new ChunkEntitySlices( final ChunkEntitySlices ret = new ChunkEntitySlices(
this.world, chunkX, chunkZ, this.world, chunkX, chunkZ, ticking ? FullChunkStatus.ENTITY_TICKING : FullChunkStatus.FULL, null,
ticking ? FullChunkStatus.ENTITY_TICKING : FullChunkStatus.FULL, null,
WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world) WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
); );

View File

@@ -7,9 +7,14 @@ import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.ThrownEnderpearl;
import net.minecraft.world.level.entity.LevelCallback; import net.minecraft.world.level.entity.LevelCallback;
public final class ServerEntityLookup extends EntityLookup { public final class ServerEntityLookup extends EntityLookup {
@@ -19,9 +24,18 @@ public final class ServerEntityLookup extends EntityLookup {
private final ServerLevel serverWorld; private final ServerLevel serverWorld;
public final ReferenceList<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker public final ReferenceList<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
// Vanilla does not increment ticket timeouts if the chunk is progressing in generation. They made this change in 1.21.6 so that the ender pearl
// ticket does not expire if the chunk fails to generate before the timeout expires. Rather than blindly adjusting the entire system behavior
// to fix this small issue, we instead add non-expirable tickets here to keep ender pearls ticking. This is how the original feature should have
// been implemented, but I don't think Vanilla has proper entity add/remove hooks like we do. Fixes MC-297591
private static final TicketType ENDER_PEARL_TICKER = ChunkSystemTicketType.create("chunk_system:ender_pearl_ticker", null);
private final Long2IntOpenHashMap enderPearlChunkCount = new Long2IntOpenHashMap();
private final boolean keepEnderPearlsTicking;
public ServerEntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) { public ServerEntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
super(world, worldCallback); super(world, worldCallback);
this.serverWorld = world; this.serverWorld = world;
this.keepEnderPearlsTicking = PlatformHooks.get().addTicketForEnderPearls(world);
} }
@Override @Override
@@ -63,6 +77,10 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ServerPlayer player) { if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().tickPlayer(player); ((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().tickPlayer(player);
} }
if (entity instanceof ThrownEnderpearl enderpearl && (oldSectionX != newSectionX || oldSectionZ != newSectionZ)) {
this.removeEnderPearl(CoordinateUtils.getChunkKey(oldSectionX, oldSectionZ));
this.addEnderPearl(CoordinateUtils.getChunkKey(newSectionX, newSectionZ));
}
PlatformHooks.get().entityMove( PlatformHooks.get().entityMove(
entity, entity,
CoordinateUtils.getChunkSectionKey(oldSectionX, oldSectionY, oldSectionZ), CoordinateUtils.getChunkSectionKey(oldSectionX, oldSectionY, oldSectionZ),
@@ -75,6 +93,9 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ServerPlayer player) { if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().addPlayer(player); ((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().addPlayer(player);
} }
if (entity instanceof ThrownEnderpearl enderpearl) {
this.addEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()));
}
} }
@Override @Override
@@ -82,6 +103,9 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ServerPlayer player) { if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().removePlayer(player); ((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().removePlayer(player);
} }
if (entity instanceof ThrownEnderpearl enderpearl) {
this.removeEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()));
}
} }
@Override @Override
@@ -112,4 +136,31 @@ public final class ServerEntityLookup extends EntityLookup {
protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) { protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) {
return PlatformHooks.get().screenEntity(this.serverWorld, entity, fromDisk, event); return PlatformHooks.get().screenEntity(this.serverWorld, entity, fromDisk, event);
} }
private void addEnderPearl(final long coordinate) {
if (!this.keepEnderPearlsTicking) {
return;
}
final int oldCount = this.enderPearlChunkCount.addTo(coordinate, 1);
if (oldCount != 0) {
return;
}
((ChunkSystemServerLevel)this.serverWorld).moonrise$getChunkTaskScheduler().chunkHolderManager
.addTicketAtLevel(ENDER_PEARL_TICKER, coordinate, ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL, null);
}
private void removeEnderPearl(final long coordinate) {
if (!this.keepEnderPearlsTicking) {
return;
}
final int oldCount = this.enderPearlChunkCount.addTo(coordinate, -1);
if (oldCount != 1) {
return;
}
this.enderPearlChunkCount.remove(coordinate);
((ChunkSystemServerLevel)this.serverWorld).moonrise$getChunkTaskScheduler().chunkHolderManager
.removeTicketAtLevel(ENDER_PEARL_TICKER, coordinate, ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL, null);
}
} }

View File

@@ -119,7 +119,7 @@ public final class PoiChunk {
final CompoundTag sections = new CompoundTag(); final CompoundTag sections = new CompoundTag();
ret.put("Sections", sections); ret.put("Sections", sections);
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); ret.putInt("DataVersion", SharedConstants.getCurrentVersion().dataVersion().version());
final ServerLevel world = this.world; final ServerLevel world = this.world;
final int chunkX = this.chunkX; final int chunkX = this.chunkX;

View File

@@ -47,12 +47,16 @@ import java.util.function.Function;
public final class RegionizedPlayerChunkLoader { public final class RegionizedPlayerChunkLoader {
public static final TicketType PLAYER_TICKET = ChunkSystemTicketType.create("chunk_system:player_ticket", Long::compareTo); public static final TicketType PLAYER_TICKET = ChunkSystemTicketType.create("chunk_system:player_ticket", Long::compareTo);
public static final TicketType PLAYER_TICKET_DELAYED = ChunkSystemTicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5L * 20L); public static final TicketType PLAYER_TICKET_DELAYED = ChunkSystemTicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 1L);
public static final int GENERATED_TICKET_LEVEL = ChunkHolderManager.FULL_LOADED_TICKET_LEVEL; public static final int GENERATED_TICKET_LEVEL = ChunkHolderManager.FULL_LOADED_TICKET_LEVEL;
public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY); public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
public static final int TICK_TICKET_LEVEL = ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL; public static final int TICK_TICKET_LEVEL = ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL;
public static void setUnloadDelay(final long ticks) {
((ChunkSystemTicketType)(Object)PLAYER_TICKET_DELAYED).moonrise$setTimeout(Math.max(1, ticks));
}
public static final class ViewDistanceHolder { public static final class ViewDistanceHolder {
private volatile ViewDistances viewDistances; private volatile ViewDistances viewDistances;
@@ -147,7 +151,7 @@ public final class RegionizedPlayerChunkLoader {
} }
public static int getAPITickViewDistance(final ServerPlayer player) { public static int getAPITickViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel(); final ServerLevel level = player.level();
final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader(); final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (data == null) { if (data == null) {
return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPITickDistance(); return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPITickDistance();
@@ -156,7 +160,7 @@ public final class RegionizedPlayerChunkLoader {
} }
public static int getAPIViewDistance(final ServerPlayer player) { public static int getAPIViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel(); final ServerLevel level = player.level();
final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader(); final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (data == null) { if (data == null) {
return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPIViewDistance(); return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPIViewDistance();
@@ -166,7 +170,7 @@ public final class RegionizedPlayerChunkLoader {
} }
public static int getAPISendViewDistance(final ServerPlayer player) { public static int getAPISendViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel(); final ServerLevel level = player.level();
final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader(); final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (data == null) { if (data == null) {
return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPISendViewDistance(); return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPISendViewDistance();

View File

@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling; package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock; import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable; import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import ca.spottedleaf.concurrentutil.util.Priority; import ca.spottedleaf.concurrentutil.util.Priority;
@@ -82,6 +83,7 @@ public final class ChunkHolderManager {
private long currentTick; private long currentTick;
private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>(); private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>();
private final MultiThreadedQueue<NewChunkHolder> offThreadPendingFullLoadUpdate = new MultiThreadedQueue<>();
private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> { private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
if (c1 == c2) { if (c1 == c2) {
return 0; return 0;
@@ -112,20 +114,20 @@ public final class ChunkHolderManager {
this.unloadQueue = new ChunkUnloadQueue(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift()); this.unloadQueue = new ChunkUnloadQueue(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift());
} }
public boolean processTicketUpdates(final int posX, final int posZ) { public boolean processTicketUpdates(final int chunkX, final int chunkZ) {
final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT; final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
final int ticketMask = (1 << ticketShift) - 1; final int ticketMask = (1 << ticketShift) - 1;
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>(); final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>(); final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
final boolean ret; final boolean ret;
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock( final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
((posX >> ticketShift) - 1) << ticketShift, ((chunkX >> ticketShift) - 1) << ticketShift,
((posZ >> ticketShift) - 1) << ticketShift, ((chunkZ >> ticketShift) - 1) << ticketShift,
(((posX >> ticketShift) + 1) << ticketShift) | ticketMask, (((chunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((posZ >> ticketShift) + 1) << ticketShift) | ticketMask (((chunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
); );
try { try {
ret = this.processTicketUpdatesNoLock(posX >> ticketShift, posZ >> ticketShift, scheduledTasks, changedFullStatus); ret = this.processTicketUpdatesNoLock(chunkX >> ticketShift, chunkZ >> ticketShift, scheduledTasks, changedFullStatus);
} finally { } finally {
this.ticketLockArea.unlock(ticketLock); this.ticketLockArea.unlock(ticketLock);
} }
@@ -805,6 +807,9 @@ public final class ChunkHolderManager {
return removeDelay <= 0L; return removeDelay <= 0L;
}; };
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) { for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) {
final long sectionKey = iterator.nextLong(); final long sectionKey = iterator.nextLong();
@@ -813,9 +818,16 @@ public final class ChunkHolderManager {
continue; continue;
} }
final int lowerChunkX = CoordinateUtils.getChunkX(sectionKey) << sectionShift;
final int lowerChunkZ = CoordinateUtils.getChunkZ(sectionKey) << sectionShift;
final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
final int ticketMask = (1 << ticketShift) - 1;
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock( final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
CoordinateUtils.getChunkX(sectionKey) << sectionShift, ((lowerChunkX >> ticketShift) - 1) << ticketShift,
CoordinateUtils.getChunkZ(sectionKey) << sectionShift ((lowerChunkZ >> ticketShift) - 1) << ticketShift,
(((lowerChunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((lowerChunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
); );
try { try {
@@ -862,9 +874,23 @@ public final class ChunkHolderManager {
if (chunkToExpireCount.isEmpty()) { if (chunkToExpireCount.isEmpty()) {
this.sectionToChunkToExpireCount.remove(sectionKey); this.sectionToChunkToExpireCount.remove(sectionKey);
} }
// In order to prevent a race condition where an off-thread invokes processTicketUpdates(), we need to process ticket updates here
// so that we catch any additions to the changed full status list. If an off-thread were to process tickets here, it would not be guaranteed
// that it would be added to the full changed status set by the end of the call - possibly allowing ticket level decreases to be processed
// outside of this call, which is not an intended or expected of this chunk system.
this.processTicketUpdatesNoLock(lowerChunkX >> ThreadedTicketLevelPropagator.SECTION_SHIFT, lowerChunkZ >> ThreadedTicketLevelPropagator.SECTION_SHIFT, scheduledTasks, changedFullStatus);
} finally { } finally {
this.ticketLockArea.unlock(ticketLock); this.ticketLockArea.unlock(ticketLock);
} }
this.addChangedStatuses(changedFullStatus);
changedFullStatus.clear(); // clear for next loop iteration
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
scheduledTasks.get(i).schedule();
}
scheduledTasks.clear(); // clear for next loop iteration
} }
this.processTicketUpdates(); this.processTicketUpdates();
@@ -1091,14 +1117,9 @@ public final class ChunkHolderManager {
return; return;
} }
if (!TickThread.isTickThread()) { if (!TickThread.isTickThread()) {
this.taskScheduler.scheduleChunkTask(() -> { // These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate; // which will invoke processTicketUpdates
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus);
pendingFullLoadUpdate.add(changedFullStatus.get(i));
}
ChunkHolderManager.this.processPendingFullUpdate();
}, Priority.HIGHEST);
} else { } else {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate; final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) { for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
@@ -1379,36 +1400,20 @@ public final class ChunkHolderManager {
} }
public boolean processTicketUpdates() { public boolean processTicketUpdates() {
return this.processTicketUpdates(true, null);
}
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
return CURRENT_TICKET_UPDATE_SCHEDULING.get();
}
private boolean processTicketUpdates(final boolean processFullUpdates, List<ChunkProgressionTask> scheduledTasks) {
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) { if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager"); throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
} }
if (!PlatformHooks.get().allowAsyncTicketUpdates() && !TickThread.isTickThread()) { final boolean isTickThread = TickThread.isTickThread();
if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) {
TickThread.ensureTickThread("Cannot asynchronously process ticket updates"); TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
} }
List<NewChunkHolder> changedFullStatus = null;
final boolean isTickThread = TickThread.isTickThread();
boolean ret = false; boolean ret = false;
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
final boolean canProcessScheduling = scheduledTasks == null;
if (this.ticketLevelPropagator.hasPendingUpdates()) { if (this.ticketLevelPropagator.hasPendingUpdates()) {
if (scheduledTasks == null) { final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
scheduledTasks = new ArrayList<>(); final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
}
changedFullStatus = new ArrayList<>();
this.blockTicketUpdates(); this.blockTicketUpdates();
try { try {
@@ -1419,27 +1424,42 @@ public final class ChunkHolderManager {
} finally { } finally {
this.unblockTicketUpdates(Boolean.FALSE); this.unblockTicketUpdates(Boolean.FALSE);
} }
}
if (changedFullStatus != null) {
this.addChangedStatuses(changedFullStatus); this.addChangedStatuses(changedFullStatus);
}
if (canProcessScheduling && scheduledTasks != null) {
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) { for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
scheduledTasks.get(i).schedule(); scheduledTasks.get(i).schedule();
} }
} }
if (canProcessFullUpdates) { if (isTickThread) {
ret |= this.processPendingFullUpdate(); ret |= this.processPendingFullUpdate();
} }
return ret; return ret;
} }
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
return CURRENT_TICKET_UPDATE_SCHEDULING.get();
}
// only call on tick thread
private void processOffThreadFullUpdates() {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
final MultiThreadedQueue<NewChunkHolder> offThreadPendingFullLoadUpdate = this.offThreadPendingFullLoadUpdate;
NewChunkHolder toUpdate;
while ((toUpdate = offThreadPendingFullLoadUpdate.poll()) != null) {
pendingFullLoadUpdate.add(toUpdate);
}
}
// only call on tick thread // only call on tick thread
private boolean processPendingFullUpdate() { private boolean processPendingFullUpdate() {
this.processOffThreadFullUpdates();
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate; final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
boolean ret = false; boolean ret = false;

View File

@@ -117,9 +117,11 @@ public final class NewChunkHolder {
if (!transientChunk) { if (!transientChunk) {
if (entityChunk != null) { if (entityChunk != null) {
final List<Entity> entities = ChunkEntitySlices.readEntities(this.world, entityChunk); final ChunkPos pos = new ChunkPos(this.chunkX, this.chunkZ);
((ChunkSystemServerLevel)this.world).moonrise$getEntityLookup().addEntityChunkEntities(entities, new ChunkPos(this.chunkX, this.chunkZ)); final List<Entity> entities = ChunkEntitySlices.readEntities(this.world, pos, entityChunk);
((ChunkSystemServerLevel)this.world).moonrise$getEntityLookup().addEntityChunkEntities(entities, pos);
} }
} }

View File

@@ -30,4 +30,5 @@ public interface ChunkSystemTicketType<T> {
public long[] moonrise$getCounterTypes(); public long[] moonrise$getCounterTypes();
public void moonrise$setTimeout(final long to);
} }

View File

@@ -2091,7 +2091,7 @@ public final class CollisionUtil {
continue; continue;
} }
if ((entity == null && otherEntity.canBeCollidedWith()) || (entity != null && entity.canCollideWith(otherEntity))) { if ((entity == null && otherEntity.canBeCollidedWith(entity)) || (entity != null && entity.canCollideWith(otherEntity))) {
if (checkOnly) { if (checkOnly) {
return true; return true;
} else { } else {

View File

@@ -177,7 +177,7 @@ accessible method net/minecraft/server/network/PlayerChunkSender sendChunk (Lnet
# ChunkStatusTasks # ChunkStatusTasks
accessible method net/minecraft/world/level/chunk/status/ChunkStatusTasks postLoadProtoChunk (Lnet/minecraft/server/level/ServerLevel;Ljava/util/List;)V accessible method net/minecraft/world/level/chunk/status/ChunkStatusTasks postLoadProtoChunk (Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/storage/ValueInput$ValueInputList;)V
accessible method net/minecraft/world/level/chunk/status/ChunkStatusTasks light (Lnet/minecraft/world/level/chunk/status/WorldGenContext;Lnet/minecraft/world/level/chunk/status/ChunkStep;Lnet/minecraft/util/StaticCache2D;Lnet/minecraft/world/level/chunk/ChunkAccess;)Ljava/util/concurrent/CompletableFuture; accessible method net/minecraft/world/level/chunk/status/ChunkStatusTasks light (Lnet/minecraft/world/level/chunk/status/WorldGenContext;Lnet/minecraft/world/level/chunk/status/ChunkStep;Lnet/minecraft/util/StaticCache2D;Lnet/minecraft/world/level/chunk/ChunkAccess;)Ljava/util/concurrent/CompletableFuture;

View File

@@ -54,6 +54,7 @@
"chunk_system.TicketMixin", "chunk_system.TicketMixin",
"chunk_system.TicketStorageMixin", "chunk_system.TicketStorageMixin",
"chunk_system.TicketTypeMixin", "chunk_system.TicketTypeMixin",
"chunk_system.WaypointTransmitterMixin",
"chunk_tick_iteration.ChunkMapMixin", "chunk_tick_iteration.ChunkMapMixin",
"chunk_tick_iteration.DistanceManagerMixin", "chunk_tick_iteration.DistanceManagerMixin",
"chunk_tick_iteration.ServerChunkCacheMixin", "chunk_tick_iteration.ServerChunkCacheMixin",
@@ -144,5 +145,5 @@
}, },
"overwrites": { "overwrites": {
"conformVisibility": true "conformVisibility": true
} }
} }