Compare commits

...

64 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
Spottedleaf
b15e8398e7 Set version to 0.3.0-SNAPSHOT 2025-05-07 17:03:51 -07:00
Spottedleaf
b6410354e5 Set version to 0.3.0-beta.1 2025-05-07 17:01:31 -07:00
Spottedleaf
85ca21eb28 Update dependencies, fix fabric mixins 2025-05-07 17:00:02 -07:00
Spottedleaf
bda7cfaad9 Update fabric-loom 2025-04-01 19:35:53 -07:00
Spottedleaf
fece86b279 Copy fixes from Paper update
1. Use the provided ticket's identifier when routing to the new
   chunk system. Not needed in Moonrise but Paper may set the
   identifier.
2. Mark TicketStorage as dirty after ticking it
2025-04-01 18:49:06 -07:00
Spottedleaf
27759719e9 Update to 1.21.5 2025-04-01 15:39:19 -07:00
Spottedleaf
13948cdf26 Set version to 0.2.0-SNAPSHOT 2025-02-24 21:09:35 -08:00
Spottedleaf
ac0c7deb43 Set version to 0.2.0-beta.9 2025-02-24 21:06:48 -08:00
Jason Penilla
16c8398d8a Clear lastSection on game event listener removal (fixes #87) (#99) 2025-02-24 17:39:07 -07:00
Spottedleaf
661ef813bb Add further information to thread check errors
The entity data is more complete, which will help debug problems
on Folia.
2025-01-28 13:34:32 -08:00
Spottedleaf
cf1d26a73c Set version to 0.2.0-SNAPSHOT 2025-01-27 13:59:26 -08:00
Spottedleaf
0cbff02a1c Set version to 0.2.0-beta.8 2025-01-27 13:57:16 -08:00
Spottedleaf
ce4ee767fe Correctly retrun true for empty input shapes in EntityGetter#isUnobstructed
Vanilla will return true for empty shapes, so we should as well.
2025-01-27 07:51:49 -08:00
Jason Penilla
d31b15122f build: Sort AT output instead of copying and modifying AT classes 2025-01-17 19:38:38 -07:00
Jason Penilla
ca931e842b build: Enable config cache, replace deprecated space assignment use (#92) 2025-01-17 19:24:18 -07:00
Spottedleaf
c2cf985899 Update to ConcurrentUtil 0.0.3 2025-01-11 06:26:19 -08:00
Spottedleaf
d270cf06d9 Log thread check parameters when the thread check fails
This provides additional debug information that may be useful.
2025-01-11 04:52:21 -08:00
Spottedleaf
09735958c0 Set version to 0.2.0-SNAPSHOT 2024-12-23 00:06:18 -08:00
Spottedleaf
6ec14ff755 Set version to 0.2.0-beta.7 2024-12-23 00:04:55 -08:00
Jason Penilla
18e872ad12 Compile against latest NeoForge and misc build updates 2024-12-20 19:12:51 -08:00
Spottedleaf
13c6499854 Set version to 0.2.0-SNAPSHOT 2024-12-20 11:33:39 -08:00
Spottedleaf
b70443edd5 Set version to 0.2.0-beta.6 2024-12-20 11:33:26 -08:00
Spottedleaf
38bab21ddf Update cloth config version for 1.21.4 2024-12-20 11:31:41 -08:00
Spottedleaf
f7d98327e0 Move ChunkSystem class to PlatformHooks
This is required for Paper's hard fork, as the Moonrise patch
needs to replace the base implementation of the chunk system
hooks.
2024-12-16 10:09:43 -08:00
Jason Penilla
271a58d2af Exclude transitive deps provided by Minecraft 2024-12-04 10:17:10 -07:00
Spottedleaf
a4f5ef1abd Add ConcurrentUtil and YamlConfig to .gitignore 2024-12-04 02:21:25 -08:00
Spottedleaf
d87df61412 Apply coordinate offset only to VoxelShape
VoxelShape coordiantes generally are an integer + a sum of powers of
two between [-1, -3]. Most offsets are generally an integer. As
a result, applying an offset to the coordinates generally results
in an error of 0. However, coordinate inputs do not follow such
trends. Thus, when applying an offset to the coordinate input,
there may be some floating point error.

By applying the offset to the VoxelShape coordinates, we can
eliminate additional floating point error.

This change also fixes the inconsistency when using
the single AABB, as input coordinates were not offset
when using the single AABB as the single AABB is already
offset.

Fixes https://github.com/Tuinity/Moonrise/issues/81
This specific issue is caused by floating point error resulting
in the falling anvil's y position becoming around -8E-17 when it
should be 0.
While this is still very comfortably in the collision
epsilon (1.0E-7), this results in the falling anvil's y block
position to become -1 (as the block position is simply
the floor of the coordinate).
2024-12-04 02:20:48 -08:00
Jason Penilla
e917272d3a Use libraries from Paper repo (#82)
* Use YamlConfig from Paper repo

* Add comment to settings

* Use ConcurrentUtil from Paper repo and use release of YamlConfig

* Use ConcurrentUtil release
2024-12-03 20:19:06 -07:00
Jason Penilla
bff8caea54 Update modmenu and use fabric api bom
Fixes fabric runs for 1.21.4
2024-12-03 17:08:09 -07:00
Jason Penilla
97a865c532 update lithium versions for 1.21.4 2024-12-03 12:37:32 -07:00
Spottedleaf
f6541b0d91 Fix AcquirePoiMixin
Just needed to update the method targets
2024-12-03 11:28:23 -08:00
Jason Penilla
6b4139a5ab add neoforge test 2024-12-03 11:58:07 -07:00
Jason Penilla
43164acf5e Add Mixin audit unit test for Fabric
Doing this for NeoForge may need to wait until we build using ModDevGradle
2024-12-03 11:58:07 -07:00
Jason Penilla
73d73c935a temporary fix for reproducible at files 2024-12-03 11:57:49 -07:00
Jason Penilla
7863c556f9 fix archive extension 2024-12-03 11:57:48 -07:00
Jason Penilla
c0b02ea709 fix jarjar being missing from final neoforge jar 2024-12-03 11:57:48 -07:00
Jason Penilla
9004f12be6 remove todo 2024-12-03 11:57:48 -07:00
Jason Penilla
b2144a55aa use asm fix from adventure-platform-mod 2024-12-03 11:57:48 -07:00
Jason Penilla
882d733203 More work on runs 2024-12-03 11:57:48 -07:00
Jason Penilla
9b6982bf65 Work on fixing runs 2024-12-03 11:57:48 -07:00
Jason Penilla
5635373cff Replace AT task dependency workaround 2024-12-03 11:57:47 -07:00
Jason Penilla
76d2c36481 WIP: Use ModDevGradle instead of archloom for common and NeoForge 2024-12-03 11:57:47 -07:00
Jason Penilla
ca791ddc74 update metadata for 1.21.4 2024-12-03 11:57:33 -07:00
Spottedleaf
7f08c11a11 Start update to 1.21.4
Remove the EnderDragon entity retrieval inside ChunkEntitySlices
and move it to PlatformHooks#addToGetEntities for fabric (neoforge
already did this).
2024-12-03 10:48:55 -08:00
Spottedleaf
93b908350e Throw when sync loading chunks after shutdown
The caller would block indefinitely if a sync load is requested
during shutdown. Rather than block indefinitely, we should throw
an exception to indicate to the caller that the chunk system is
unable to process requests.
2024-12-02 23:13:22 -08:00
Spottedleaf
4bd7eb8b72 Set version to 0.2.0-SNAPSHOT 2024-12-01 16:05:06 -08:00
106 changed files with 2557 additions and 1476 deletions

View File

@@ -31,8 +31,6 @@ jobs:
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-project-local-gradle-caches-
- name: "setup dependencies"
run: ./install_deps.sh
- name: "execute gradle build"
run: ./gradlew build
- name: Determine Snapshot Status

3
.gitignore vendored
View File

@@ -116,3 +116,6 @@ run/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
ConcurrentUtil/
YamlConfig/

6
.gitmodules vendored
View File

@@ -1,6 +0,0 @@
[submodule "ConcurrentUtil"]
path = ConcurrentUtil
url = https://github.com/Spottedleaf/ConcurrentUtil.git
[submodule "YamlConfig"]
path = YamlConfig
url = https://github.com/Spottedleaf/YamlConfig.git

Submodule ConcurrentUtil deleted from 08d3ca3241

Submodule YamlConfig deleted from 67552e7707

View File

@@ -1,41 +1,53 @@
import me.modmuss50.mpp.ReleaseType
plugins {
id("xyz.jpenilla.quiet-architectury-loom")
id("me.modmuss50.mod-publish-plugin") version "0.7.2" apply false
id("java-library")
id("net.neoforged.moddev")
id("me.modmuss50.mod-publish-plugin") version "0.8.4" apply false
}
/*
* Gets the version name from the latest Git tag
*/
// https://stackoverflow.com/questions/28498688/gradle-script-to-autoversion-and-include-the-commit-hash-in-android
def getGitCommit = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
extensions.create("runConfigCommon", RunConfigCommon.class)
def getGitCommit = providers.exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
}.standardOutput.getAsText().map { it.trim() }
def aw2at = Aw2AtTask.configureDefault(
getProject(),
layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
sourceSets.main
)
neoForge {
neoFormVersion = neoform_version
validateAccessTransformers = true
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
}
runConfigCommon {
systemProperties.put "mixin.debug", "true"
systemProperties.put "Moonrise.MaxViewDistance", "128"
}
dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
compileOnly "net.fabricmc:sponge-mixin:0.15.4+mixin.0.8.7"
compileOnly "io.github.llamalad7:mixinextras-common:0.4.1"
// work around minecraft (MDG) forcing ASM 9.3 which is incompatible with the above deps...
components.withModule("net.neoforged:minecraft-dependencies", RemoveAsmConstraint.class)
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
// todo: does cloth publish a platform-agnostic jar in mojang mappings?
compileOnly "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
}
File awFile = file("src/main/resources/moonrise.accesswidener")
allprojects {
group = rootProject.maven_group
version = rootProject.mod_version + "+" + getGitCommit()
version = rootProject.mod_version + "+" + getGitCommit.get()
plugins.apply("xyz.jpenilla.quiet-architectury-loom")
plugins.apply("java-library")
java {
withSourcesJar()
@@ -45,26 +57,30 @@ allprojects {
}
}
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter:${rootProject.junit_version}"
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.test {
useJUnitPlatform()
}
repositories {
mavenLocal {
maven {
url = "https://repo.papermc.io/repository/maven-public/"
mavenContent {
includeModule("ca.spottedleaf", "concurrentutil")
includeModule("ca.spottedleaf", "yamlconfig")
includeGroup("ca.spottedleaf")
}
}
maven {
url "https://api.modrinth.com/maven"
url = "https://api.modrinth.com/maven"
mavenContent {
includeGroup("maven.modrinth")
}
}
maven { url "https://maven.shedaniel.me/" }
maven { url "https://maven.terraformersmc.com/releases/" }
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
maven { url = "https://maven.shedaniel.me/" }
maven { url = "https://maven.terraformersmc.com/releases/" }
}
// make build reproducible
@@ -82,32 +98,22 @@ allprojects {
rename { "${it}_${rootProject.base.archivesName.get()}"}
}
}
loom {
accessWidenerPath = awFile
mixin {
useLegacyMixinAp = false
}
}
}
subprojects {
loom.mods {
main {
sourceSet("main")
sourceSet("main", project.rootProject)
}
}
loom.runs.all {
ideConfigGenerated true
property "mixin.debug", "true"
property "Moonrise.MaxViewDistance", "128"
}
plugins.apply("me.modmuss50.mod-publish-plugin")
plugins.apply 'java-library'
plugins.apply 'com.gradleup.shadow'
configurations.create("libs")
configurations.shadow {
extendsFrom(configurations.libs)
}
configurations.implementation {
extendsFrom(configurations.libs)
}
publishMods {
file = remapJar.archiveFile
if (project.version.contains("-beta.")) {
type = ReleaseType.BETA
} else {
@@ -131,20 +137,7 @@ subprojects {
}
// Setup a run with lithium for compatibility testing
sourceSets.create("lithium")
configurations.create("lithium")
loom {
createRemapConfigurations(sourceSets.lithium)
runs {
register("lithiumClient") {
client()
property "mixin.debug", "true"
}
}
}
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
}
dependencies {
String coordinates = "maven.modrinth:lithium:"
if (getProject().name == "Moonrise-NeoForge") {
@@ -152,10 +145,6 @@ subprojects {
} else {
coordinates += rootProject.fabric_lithium_version
}
modLithiumRuntimeOnly coordinates
lithium coordinates
}
}
loom.runs.all {
ideConfigGenerated false
}

11
buildSrc/build.gradle.kts Normal file
View File

@@ -0,0 +1,11 @@
repositories {
gradlePluginPortal()
mavenCentral()
maven("https://maven.fabricmc.net/")
maven("https://maven.architectury.dev/")
}
dependencies {
implementation("net.fabricmc:access-widener:2.1.0")
implementation("dev.architectury:at:1.0.1")
}

View File

@@ -0,0 +1,152 @@
import dev.architectury.at.AccessChange;
import dev.architectury.at.AccessTransform;
import dev.architectury.at.AccessTransformSet;
import dev.architectury.at.ModifierChange;
import dev.architectury.at.io.AccessTransformFormat;
import dev.architectury.at.io.AccessTransformFormats;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import net.fabricmc.accesswidener.AccessWidenerReader;
import net.fabricmc.accesswidener.AccessWidenerVisitor;
import org.cadixdev.bombe.type.signature.MethodSignature;
import org.gradle.api.DefaultTask;
import org.gradle.api.Project;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.TaskProvider;
@CacheableTask
public abstract class Aw2AtTask extends DefaultTask {
@InputFile
@PathSensitive(PathSensitivity.NONE)
public abstract RegularFileProperty getInputFile();
@OutputFile
public abstract RegularFileProperty getOutputFile();
@Inject
public abstract ProjectLayout getLayout();
public static TaskProvider<Aw2AtTask> configureDefault(
final Project project,
final File awFile,
final SourceSet sourceSet
) {
final TaskProvider<Aw2AtTask> aw2at = project.getTasks().register("aw2at", Aw2AtTask.class, task -> {
task.getOutputFile().set(project.getLayout().getBuildDirectory().file("aw2at/files/accesstransformer.cfg"));
task.getInputFile().set(awFile);
});
final TaskProvider<CopyTask> copyTask = project.getTasks().register("copyAt", CopyTask.class, copy -> {
copy.getInputFile().set(aw2at.flatMap(Aw2AtTask::getOutputFile));
copy.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir("aw2at/dir"));
copy.getDestination().set("META-INF/accesstransformer.cfg");
});
sourceSet.resources(resources -> {
resources.srcDir(copyTask.flatMap(CopyTask::getOutputDirectory));
});
return aw2at;
}
@TaskAction
public void run() {
try (final BufferedReader reader = Files.newBufferedReader(this.getInputFile().get().getAsFile().toPath())) {
final AccessTransformSet accessTransformSet = toAccessTransformSet(reader);
Files.deleteIfExists(this.getOutputFile().get().getAsFile().toPath());
Files.createDirectories(this.getOutputFile().get().getAsFile().toPath().getParent());
writeLF(AccessTransformFormats.FML, this.getOutputFile().get().getAsFile().toPath(), accessTransformSet);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
private static void writeLF(final AccessTransformFormat format, final Path path, final AccessTransformSet at) throws IOException {
final StringWriter stringWriter = new StringWriter();
final BufferedWriter writer = new BufferedWriter(stringWriter);
format.write(writer, at);
writer.close();
final List<String> lines = Arrays.stream(stringWriter.toString()
// unify line endings
.replace("\r\n", "\n")
.split("\n"))
// skip blank lines
.filter(it -> !it.isBlank())
// sort
.sorted()
.toList();
Files.writeString(path, String.join("\n", lines));
}
// Below methods are heavily based on architectury-loom Aw2At class (MIT licensed)
/*
MIT License
Copyright (c) 2016 FabricMC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
public static AccessTransformSet toAccessTransformSet(final BufferedReader reader) throws IOException {
AccessTransformSet atSet = AccessTransformSet.create();
new AccessWidenerReader(new AccessWidenerVisitor() {
@Override
public void visitClass(final String name, final AccessWidenerReader.AccessType access, final boolean transitive) {
atSet.getOrCreateClass(name).merge(toAt(access));
}
@Override
public void visitMethod(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) {
atSet.getOrCreateClass(owner).mergeMethod(MethodSignature.of(name, descriptor), toAt(access));
}
@Override
public void visitField(final String owner, final String name, final String descriptor, final AccessWidenerReader.AccessType access, final boolean transitive) {
atSet.getOrCreateClass(owner).mergeField(name, toAt(access));
}
}).read(reader);
return atSet;
}
public static AccessTransform toAt(final AccessWidenerReader.AccessType access) {
return switch (access) {
case ACCESSIBLE -> AccessTransform.of(AccessChange.PUBLIC);
case EXTENDABLE, MUTABLE -> AccessTransform.of(AccessChange.PUBLIC, ModifierChange.REMOVE);
};
}
}

View File

@@ -0,0 +1,44 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.stream.Stream;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;
public abstract class CopyTask extends DefaultTask {
@InputFile
public abstract RegularFileProperty getInputFile();
@Input
public abstract Property<String> getDestination();
@OutputDirectory
public abstract DirectoryProperty getOutputDirectory();
@TaskAction
public void run() {
final Path outputDirPath = this.getOutputDirectory().get().getAsFile().toPath();
try {
try (final Stream<Path> walk = Files.walk(outputDirPath)) {
for (final Path path : walk.sorted(Comparator.reverseOrder()).toList()) {
Files.delete(path);
}
}
final Path destFile = outputDirPath.resolve(this.getDestination().get());
Files.createDirectories(destFile.getParent());
Files.copy(this.getInputFile().get().getAsFile().toPath(), destFile);
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,24 @@
import java.util.Objects;
import org.gradle.api.artifacts.CacheableRule;
import org.gradle.api.artifacts.ComponentMetadataContext;
import org.gradle.api.artifacts.ComponentMetadataRule;
@CacheableRule
public abstract class RemoveAsmConstraint implements ComponentMetadataRule {
@Override
public void execute(final ComponentMetadataContext ctx) {
ctx.getDetails().allVariants(variants -> {
variants.withDependencies(deps -> {
deps.forEach(dep -> {
if (Objects.equals(dep.getGroup(), "org.ow2.asm")) {
if (dep.getVersionConstraint().getStrictVersion() != null) {
dep.version(v -> {
v.require(v.getStrictVersion());
});
}
}
});
});
});
}
}

View File

@@ -0,0 +1,5 @@
import org.gradle.api.provider.MapProperty;
public abstract class RunConfigCommon {
public abstract MapProperty<String, String> getSystemProperties();
}

View File

@@ -1,45 +1,71 @@
import java.util.stream.Collectors
import net.fabricmc.loom.util.gradle.SourceSetHelper
plugins {
id("xyz.jpenilla.quiet-architectury-loom")
id("quiet-fabric-loom")
id 'maven-publish'
id 'com.gradleup.shadow'
}
configurations.create("libs")
configurations.shadow {
extendsFrom(configurations.libs)
}
configurations.implementation {
extendsFrom(configurations.libs)
boolean gui = enable_gui == "true"
if (gui) {
sourceSets.create("gui")
loom.createRemapConfigurations(sourceSets.gui)
}
dependencies {
add('shadow', project([path: ":", configuration: "namedElements"]))
runtimeOnly(project(":").sourceSets.main.output)
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
runtimeOnly(project(":").sourceSets.main.output)
shadow(project(":"))
compileOnly(project(":"))
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
if (gui) {
guiCompileOnly(project(":"))
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 fabricApiLibs.command.api.v2
modImplementation fabricApiLibs.lifecycle.events.v1
include fabricApiLibs.command.api.v2
include fabricApiLibs.base
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
if (gui) {
afterEvaluate {
configurations.guiCompileOnly {
extendsFrom configurations.getByName("minecraftNamedCompile")
}
}
}
shadowJar {
tasks.processResources {
def properties = [
"version": project.version,
"minecraft_version": minecraft_version,
"loader_version": loader_version,
"mod_version": mod_version
]
inputs.properties(properties)
filesMatching("fabric.mod.json") {
expand properties
}
}
tasks.shadowJar {
archiveClassifier = "dev-all"
destinationDirectory = layout.buildDirectory.dir("libs")
configurations = [project.configurations.shadow]
@@ -49,6 +75,7 @@ shadowJar {
}
publishMods {
file = remapJar.archiveFile
modLoaders = ["fabric"]
modrinth {
@@ -66,3 +93,52 @@ publishMods {
)
}
}
loom {
accessWidenerPath.set(getRootProject().file("src/main/resources/moonrise.accesswidener"))
mixin {
useLegacyMixinAp = false
}
runs.configureEach {
ideConfigGenerated true
}
mods {
main {
sourceSet("main")
sourceSet("main", project.rootProject)
}
}
}
tasks.test {
def classPathGroups = SourceSetHelper.getClasspath(loom.mods.main, getProject()).stream()
.map(File.&getAbsolutePath)
.collect(Collectors.joining(File.pathSeparator))
systemProperty("fabric.classPathGroups", classPathGroups)
}
afterEvaluate {
loom.runs.configureEach { cfg ->
runConfigCommon.systemProperties.get().each {
cfg.property it.key, it.value
}
}
}
// Setup a run with lithium for compatibility testing
sourceSets.create("lithium")
loom {
createRemapConfigurations(sourceSets.lithium)
runs {
register("lithiumClient") {
client()
}
}
}
configurations.modLithiumRuntimeOnly {
extendsFrom configurations.lithium
}
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
}

View File

@@ -1,10 +1,14 @@
package ca.spottedleaf.moonrise.fabric;
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrays;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.BlockPos;
@@ -14,7 +18,10 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
@@ -26,14 +33,19 @@ import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.phys.AABB;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
public final class FabricHooks implements PlatformHooks {
public final class FabricHooks extends BaseChunkSystemHooks implements PlatformHooks {
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public String getBrand() {
return "Moonrise";
@@ -111,13 +123,43 @@ public final class FabricHooks implements PlatformHooks {
@Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) {
final Collection<EnderDragonPart> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final EnderDragonPart part : parts) {
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
into.add(part);
}
}
}
@Override
public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest, final AABB boundingBox,
final Predicate<? super T> predicate, final List<? super T> into, final int maxCount) {
if (into.size() >= maxCount) {
// fix neoforge issue: do not add if list is already full
return;
}
final Collection<EnderDragonPart> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final EnderDragonPart part : parts) {
if (!part.getBoundingBox().intersects(boundingBox)) {
continue;
}
final T casted = (T)entityTypeTest.tryCast(part);
if (casted != null && (predicate == null || predicate.test(casted))) {
into.add(casted);
if (into.size() >= maxCount) {
break;
}
}
}
}
@Override
@@ -215,11 +257,23 @@ public final class FabricHooks implements PlatformHooks {
@Override
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
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
@Override
public long[] getCounterTypesUncached(final TicketType type) {
return type == TicketType.FORCED ? new long[] { ChunkSystemTicketType.COUNTER_TYPE_FORCED } : LongArrays.EMPTY_ARRAY;
}
@Override
public boolean addTicketForEnderPearls(final ServerLevel world) {
return true;
}
}

View File

@@ -1,33 +0,0 @@
package ca.spottedleaf.moonrise.fabric.mixin.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@Mixin(DistanceManager.class)
abstract class FabricDistanceManagerMixin implements ChunkSystemDistanceManager {
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public <T> void addRegionTicket(final TicketType<T> type, final ChunkPos pos, final int radius, final T identifier) {
this.moonrise$getChunkHolderManager().addTicketAtLevel(type, pos, ChunkLevel.byStatus(FullChunkStatus.FULL) - radius, identifier);
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public <T> void removeRegionTicket(final TicketType<T> type, final ChunkPos pos, final int radius, final T identifier) {
this.moonrise$getChunkHolderManager().removeTicketAtLevel(type, pos, ChunkLevel.byStatus(FullChunkStatus.FULL) - radius, identifier);
}
}

View File

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

View File

@@ -2,7 +2,6 @@
"parent": "moonrise.mixins.json",
"package": "ca.spottedleaf.moonrise.fabric.mixin",
"mixins": [
"chunk_system.FabricDistanceManagerMixin",
"chunk_system.FabricMinecraftServerMixin",
"chunk_system.FabricServerLevelMixin",
"collisions.EntityMixin"

View File

@@ -0,0 +1,20 @@
package ca.spottedleaf.moonrise.fabric;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.spongepowered.asm.mixin.MixinEnvironment;
class MixinAuditTest {
@BeforeAll
static void beforeAll() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
void auditMixins() {
MixinEnvironment.getCurrentEnvironment().audit();
}
}

View File

@@ -1,22 +1,28 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.3
loader_version=0.16.7
supported_minecraft_versions=1.21.3
neoforge_version=21.3.31-beta
fabric_api_version=0.107.0+1.21.3
# check these on https://fabricmc.net/develop/
minecraft_version=1.21.6
loader_version=0.16.14
supported_minecraft_versions=1.21.6
neoforge_version=21.6.11-beta
neoform_version=1.21.6-20250617.151856
fabric_api_version=0.127.1+1.21.6
snakeyaml_version=2.3
concurrentutil_version=0.0.2-SNAPSHOT
yamlconfig_version=1.0.2-SNAPSHOT
cloth_version=16.0.141
modmenu_version=12.0.0-beta.1
concurrentutil_version=0.0.3
yamlconfig_version=1.0.2
cloth_version=19.0.147
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
# version ids from modrinth
fabric_lithium_version=QhCwdt4l
neo_lithium_version=wDD955sb
fabric_lithium_version=nhc57Td2
neo_lithium_version=P5VT33Jo
# Mod Properties
mod_version=0.2.0-beta.5
mod_version=0.4.0-SNAPSHOT
maven_group=ca.spottedleaf.moonrise
archives_base_name=moonrise

Binary file not shown.

View File

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

9
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# 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.
# * 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.
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@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
@rem End local scope for the variables with windows NT shell

View File

@@ -1,12 +0,0 @@
#!/bin/bash
set -eou pipefail
git submodule update --init --recursive
cd ConcurrentUtil
mvn install
cd ..
cd YamlConfig
mvn install

View File

@@ -1,9 +1,10 @@
import net.fabricmc.loom.util.aw2at.Aw2At
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import net.neoforged.moddevgradle.internal.RunGameTask
plugins {
id("xyz.jpenilla.quiet-architectury-loom")
id("net.neoforged.moddev")
id 'maven-publish'
id 'com.gradleup.shadow'
}
repositories {
@@ -13,32 +14,66 @@ repositories {
}
}
configurations.implementation {
extendsFrom(configurations.shadow)
}
def aw2at = Aw2AtTask.configureDefault(
getProject(),
rootProject.layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
sourceSets.main
)
dependencies {
add('shadow', project([path: ":", configuration: "namedElements"]))
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
}
processResources {
inputs.property "version", project.version
filesMatching("META-INF/neoforge.mods.toml") {
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
neoForge {
version = rootProject.neoforge_version
validateAccessTransformers = true
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
mods {
moonrise {
sourceSet sourceSets.main
sourceSet rootProject.sourceSets.main
}
}
runs {
client {
client()
}
server {
server()
}
}
unitTest {
enable()
testedMod = mods.moonrise
}
}
shadowJar {
dependencies {
runtimeOnly(project(":").sourceSets.main.output)
shadow(project(":"))
compileOnly(project(":"))
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
additionalRuntimeClasspath libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
implementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
jarJar "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
}
tasks.processResources {
def properties = [
"version": project.version,
"minecraft_version": minecraft_version,
"mod_version": mod_version
]
inputs.properties(properties)
filesMatching("META-INF/neoforge.mods.toml") {
expand properties
}
}
tasks.jar {
archiveClassifier = "dev"
}
tasks.shadowJar {
archiveClassifier = "dev-all"
destinationDirectory = layout.buildDirectory.dir("libs")
configurations = [project.configurations.shadow]
@@ -47,9 +82,20 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
}
Aw2At.setup(getProject(), tasks.remapJar)
tasks.register("productionJar", Zip.class) {
archiveClassifier = ""
archiveExtension = "jar"
destinationDirectory = layout.buildDirectory.dir("libs")
from(tasks.jarJar)
from(zipTree(tasks.shadowJar.archiveFile))
}
tasks.assemble {
dependsOn tasks.productionJar
}
publishMods {
file = productionJar.archiveFile
modLoaders = ["neoforge"]
modrinth {
@@ -67,3 +113,42 @@ publishMods {
)
}
}
afterEvaluate {
neoForge.runs.configureEach { cfg ->
runConfigCommon.systemProperties.get().each {
cfg.systemProperties.put it.key, it.value
}
}
}
// Setup a run with lithium for compatibility testing
neoForge {
runs {
lithiumClient {
client()
disableIdeRun()
}
}
}
tasks.withType(RunGameTask).configureEach {
if (name == "runLithiumClient") {
return
}
def out = gameDirectory.get().getAsFile().toPath().resolve("mods/lithium-tmp.jar")
doFirst {
Files.deleteIfExists(out)
}
}
def lithium = configurations.lithium
tasks.runLithiumClient {
def out = gameDirectory.get().getAsFile().toPath().resolve("mods/lithium-tmp.jar")
doFirst {
for (File file in lithium) {
Files.copy(file.toPath(), out, StandardCopyOption.REPLACE_EXISTING)
}
}
doLast {
Files.deleteIfExists(out)
}
}

View File

@@ -1 +0,0 @@
loom.platform=neoforge

View File

@@ -1,11 +1,15 @@
package ca.spottedleaf.moonrise.neoforge;
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
@@ -13,6 +17,8 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
@@ -26,6 +32,7 @@ import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.phys.AABB;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.NeoForge;
@@ -34,10 +41,14 @@ import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.event.level.ChunkDataEvent;
import net.neoforged.neoforge.event.level.ChunkEvent;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
public final class NeoForgeHooks implements PlatformHooks {
public final class NeoForgeHooks extends BaseChunkSystemHooks implements PlatformHooks {
private static final Logger LOGGER = LogUtils.getLogger();
@Override
public String getBrand() {
@@ -114,7 +125,12 @@ public final class NeoForgeHooks implements PlatformHooks {
@Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) {
for (final PartEntity<?> part : world.getPartEntities()) {
final Collection<PartEntity<?>> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final PartEntity<?> part : parts) {
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
into.add(part);
}
@@ -129,9 +145,18 @@ public final class NeoForgeHooks implements PlatformHooks {
return;
}
for (final PartEntity<?> part : world.getPartEntities()) {
final Collection<PartEntity<?>> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final PartEntity<?> part : parts) {
if (!part.getBoundingBox().intersects(boundingBox)) {
continue;
}
final T casted = (T)entityTypeTest.tryCast(part);
if (casted != null && casted.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(casted))) {
if (casted != null && (predicate == null || predicate.test(casted))) {
into.add(casted);
if (into.size() >= maxCount) {
break;
@@ -238,11 +263,32 @@ public final class NeoForgeHooks implements PlatformHooks {
@Override
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
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
@Override
public long[] getCounterTypesUncached(final TicketType type) {
final LongArrayList ret = new LongArrayList();
if (type == TicketType.FORCED) {
ret.add(ChunkSystemTicketType.COUNTER_TYPE_FORCED);
}
if (type.forceNaturalSpawning()) {
ret.add(ChunkSystemTicketType.COUNTER_TYPER_NATURAL_SPAWNING_FORCED);
}
return ret.toLongArray();
}
@Override
public boolean addTicketForEnderPearls(final ServerLevel world) {
return true;
}
}

View File

@@ -1,87 +0,0 @@
package ca.spottedleaf.moonrise.neoforge.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.Ticket;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.SortedArraySet;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(DistanceManager.class)
abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManager {
@Shadow
@Final
private Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> forcedTickets;
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public <T> void addRegionTicket(final TicketType<T> type, final ChunkPos pos, final int radius, final T identifier, final boolean forceTicks) {
final int level = ChunkLevel.byStatus(FullChunkStatus.FULL) - radius;
this.moonrise$getChunkHolderManager().addTicketAtLevel(type, pos, level, identifier);
if (forceTicks) {
final Ticket<T> forceTicket = new Ticket<>(type, level, identifier, forceTicks);
this.forcedTickets.compute(pos.toLong(), (final Long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
final SortedArraySet<Ticket<?>> ret;
if (valueInMap != null) {
ret = valueInMap;
} else {
ret = SortedArraySet.create(4);
}
if (ret.add(forceTicket)) {
((ChunkTickServerLevel)NeoForgeDistanceManagerMixin.this.moonrise$getChunkMap().level).moonrise$addPlayerTickingRequest(
CoordinateUtils.getChunkX(keyInMap.longValue()), CoordinateUtils.getChunkZ(keyInMap.longValue())
);
}
return ret;
});
}
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public <T> void removeRegionTicket(final TicketType<T> type, final ChunkPos pos, final int radius, final T identifier, final boolean forceTicks) {
final int level = ChunkLevel.byStatus(FullChunkStatus.FULL) - radius;
this.moonrise$getChunkHolderManager().removeTicketAtLevel(type, pos, level, identifier);
if (forceTicks) {
final Ticket<T> forceTicket = new Ticket<>(type, level, identifier, forceTicks);
this.forcedTickets.computeIfPresent(pos.toLong(), (final Long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
if (valueInMap.remove(forceTicket)) {
((ChunkTickServerLevel)NeoForgeDistanceManagerMixin.this.moonrise$getChunkMap().level).moonrise$removePlayerTickingRequest(
CoordinateUtils.getChunkX(keyInMap.longValue()), CoordinateUtils.getChunkZ(keyInMap.longValue())
);
}
return valueInMap.isEmpty() ? null : valueInMap;
});
}
}
/**
* @reason Only use containsKey, as we fix the leak with this impl
* @author Spottedleaf
*/
@Overwrite
public boolean shouldForceTicks(final long chunkPos) {
return this.forcedTickets.containsKey(chunkPos);
}
}

View File

@@ -0,0 +1,69 @@
package ca.spottedleaf.moonrise.neoforge.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketStorage;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.TicketStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TicketStorage.class)
abstract class NeoForgeTicketStorageMixin implements ChunkSystemTicketStorage {
@Shadow
private LongSet chunksWithForceNaturalSpawning;
/**
* @reason Destroy old chunk system state
* @author Spottedleaf
*/
@Inject(
method = "<init>(Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V",
at = @At(
value = "RETURN"
)
)
private void destroyFields(Long2ObjectOpenHashMap p_393873_, Long2ObjectOpenHashMap p_394615_, CallbackInfo ci) {
this.chunksWithForceNaturalSpawning = null;
}
/**
* @reason The forced natural spawning chunks would be empty, as tickets should always be empty.
* We need to do this to avoid throwing immediately.
* @author Spottedleaf
*/
@Redirect(
method = "<init>(Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/TicketStorage;updateForcedNaturalSpawning()V"
)
)
private void avoidUpdatingForcedNaturalChunks(final TicketStorage instance) {}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public boolean shouldForceNaturalSpawning(final ChunkPos pos) {
final Long2IntOpenHashMap counters = ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler()
.chunkHolderManager.getTicketCounters(ChunkSystemTicketType.COUNTER_TYPER_NATURAL_SPAWNING_FORCED);
if (counters == null) {
return false;
}
return counters.containsKey(CoordinateUtils.getChunkKey(pos));
}
}

View File

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

View File

@@ -2,9 +2,9 @@
"parent": "moonrise.mixins.json",
"package": "ca.spottedleaf.moonrise.neoforge.mixin",
"mixins": [
"chunk_system.NeoForgeDistanceManagerMixin",
"chunk_system.NeoForgeMinecraftServerMixin",
"chunk_system.NeoForgeServerLevelMixin",
"chunk_system.NeoForgeTicketStorageMixin",
"collisions.EntityMixin"
],
"client": [

View File

@@ -0,0 +1,17 @@
package ca.spottedleaf.moonrise.neoforge;
import org.junit.jupiter.api.Test;
import org.spongepowered.asm.mixin.MixinEnvironment;
class MixinAuditTest {
@Test
void auditMixins() {
final ClassLoader old = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(MixinAuditTest.class.getClassLoader());
MixinEnvironment.getCurrentEnvironment().audit();
} finally {
Thread.currentThread().setContextClassLoader(old);
}
}
}

View File

@@ -13,7 +13,6 @@ pluginManagement {
maven {
name = 'jmp'
url = 'https://repo.jpenilla.xyz/snapshots'
mavenContent { snapshotsOnly() }
}
maven {
name = 'architectury'
@@ -23,9 +22,10 @@ pluginManagement {
}
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.300" apply false
id 'com.gradleup.shadow' version '8.3.0' apply false
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
id("quiet-fabric-loom") version "1.10.317" apply false
id("net.neoforged.moddev") version "2.0.80" apply false
id 'com.gradleup.shadow' version '8.3.6' apply false
}
dependencyResolutionManagement {
@@ -48,3 +48,6 @@ include("fabric")
findProject(":fabric").name = "Moonrise-Fabric"
include("neoforge")
findProject(":neoforge").name = "Moonrise-NeoForge"
// includeBuild("../YamlConfig") // Uncomment to use local YamlConfig
// includeBuild("../ConcurrentUtil") // Uncomment to use local ConcurrentUtil

View File

@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.common;
import ca.spottedleaf.moonrise.common.util.ChunkSystemHooks;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import net.minecraft.core.BlockPos;
@@ -8,7 +9,6 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
@@ -24,7 +24,7 @@ import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Predicate;
public interface PlatformHooks {
public interface PlatformHooks extends ChunkSystemHooks {
public static PlatformHooks get() {
return Holder.INSTANCE;
}
@@ -64,8 +64,6 @@ public interface PlatformHooks {
public void entityMove(final Entity entity, final long oldSection, final long newSection);
public boolean screenEntity(final ServerLevel world, final Entity entity, final boolean fromDisk, final boolean event);
public boolean configFixMC224294();
public boolean configAutoConfigSendDistance();
@@ -104,6 +102,8 @@ public interface PlatformHooks {
public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
public boolean addTicketForEnderPearls(final ServerLevel world);
public static final class 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.util.MoonriseCommon;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.yamlconfig.InitialiseHook;
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
@@ -38,7 +39,7 @@ public final class MoonriseConfig {
@Adaptable
public static final class Basic {
public static final class Basic implements InitialiseHook {
@Serializable(
comment = """
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
)
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(

View File

@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.list;
import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.NoSuchElementException;
@@ -21,15 +22,34 @@ public final class IteratorSafeOrderedReferenceSet<E> {
private int iteratorCount;
public IteratorSafeOrderedReferenceSet() {
this(16, 0.75f, 16, 0.2);
this(Object.class);
}
public IteratorSafeOrderedReferenceSet(final Class<? super E> arrComponent) {
this(16, 0.75f, 16, 0.2, arrComponent);
}
public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity,
final double maxFragFactor) {
this(setCapacity, setLoadFactor, arrayCapacity, maxFragFactor, Object.class);
}
public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity,
final double maxFragFactor, final Class<? super E> arrComponent) {
this.indexMap = new Reference2IntLinkedOpenHashMap<>(setCapacity, setLoadFactor);
this.indexMap.defaultReturnValue(-1);
this.maxFragFactor = maxFragFactor;
this.listElements = (E[])new Object[arrayCapacity];
this.listElements = (E[])Array.newInstance(arrComponent, arrayCapacity);
}
// includes null (gravestone) elements
public E[] getListRaw() {
return this.listElements;
}
// includes null (gravestone) elements
public int getListSize() {
return this.listSize;
}
/*
@@ -81,7 +101,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
public int createRawIterator() {
++this.iteratorCount;
if (this.indexMap.isEmpty()) {
return -1;
return Integer.MAX_VALUE;
} else {
return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0;
}
@@ -96,7 +116,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
}
return -1;
return Integer.MAX_VALUE;
}
public void finishRawIterator() {
@@ -205,10 +225,6 @@ public final class IteratorSafeOrderedReferenceSet<E> {
//this.check();
}
public E rawGet(final int index) {
return this.listElements[index];
}
public int size() {
// always returns the correct amount - listSize can be different
return this.indexMap.size();

View File

@@ -1,9 +1,9 @@
package ca.spottedleaf.moonrise.common.misc;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
@@ -121,8 +121,8 @@ public final class NearbyPlayers {
players[NearbyMapType.GENERAL.ordinal()].update(chunk.x, chunk.z, GENERAL_AREA_VIEW_DISTANCE);
players[NearbyMapType.GENERAL_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_SMALL_VIEW_DISTANCE);
players[NearbyMapType.GENERAL_REALLY_SMALL.ordinal()].update(chunk.x, chunk.z, GENERAL_REALLY_SMALL_VIEW_DISTANCE);
players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getTickViewDistance(player));
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getViewDistance(player));
players[NearbyMapType.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getTickViewDistance(player));
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getViewDistance(player));
players[NearbyMapType.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration
}

View File

@@ -1,6 +1,6 @@
package ca.spottedleaf.moonrise.common.misc;
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
@@ -23,12 +23,16 @@ public final class PositionCountingAreaMap<T> {
return this.positions.size();
}
public boolean hasObjectsNear(final long pos) {
return this.positions.containsKey(pos);
}
public boolean hasObjectsNear(final int toX, final int toZ) {
return this.positions.containsKey(IntPairUtil.key(toX, toZ));
return this.positions.containsKey(CoordinateUtils.getChunkKey(toX, toZ));
}
public int getObjectsNear(final int toX, final int toZ) {
return this.positions.get(IntPairUtil.key(toX, toZ));
return this.positions.get(CoordinateUtils.getChunkKey(toX, toZ));
}
public boolean add(final T parameter, final int toX, final int toZ, final int distance) {
@@ -85,12 +89,12 @@ public final class PositionCountingAreaMap<T> {
@Override
protected void addCallback(final T parameter, final int toX, final int toZ) {
PositionCountingAreaMap.this.positions.addTo(IntPairUtil.key(toX, toZ), 1);
PositionCountingAreaMap.this.positions.addTo(CoordinateUtils.getChunkKey(toX, toZ), 1);
}
@Override
protected void removeCallback(final T parameter, final int toX, final int toZ) {
final long key = IntPairUtil.key(toX, toZ);
final long key = CoordinateUtils.getChunkKey(toX, toZ);
if (PositionCountingAreaMap.this.positions.addTo(key, -1) == 1) {
PositionCountingAreaMap.this.positions.remove(key);
}

View File

@@ -1,7 +1,5 @@
package ca.spottedleaf.moonrise.common.misc;
import ca.spottedleaf.concurrentutil.util.IntegerUtil;
public abstract class SingleUserAreaMap<T> {
public static final int NOT_SET = Integer.MIN_VALUE;
@@ -99,8 +97,8 @@ public abstract class SingleUserAreaMap<T> {
final int dx = toX - fromX;
final int dz = toZ - fromZ;
final int totalX = IntegerUtil.branchlessAbs(fromX - toX);
final int totalZ = IntegerUtil.branchlessAbs(fromZ - toZ);
final int totalX = Math.abs(fromX - toX);
final int totalZ = Math.abs(fromZ - toZ);
if (Math.max(totalX, totalZ) > (2 * Math.max(newViewDistance, oldViewDistance))) {
// teleported
@@ -120,7 +118,7 @@ public abstract class SingleUserAreaMap<T> {
for (int currZ = oldMinZ; currZ <= oldMaxZ; ++currZ) {
// only remove if we're outside the new view distance...
if (Math.max(IntegerUtil.branchlessAbs(currX - toX), IntegerUtil.branchlessAbs(currZ - toZ)) > newViewDistance) {
if (Math.max(Math.abs(currX - toX), Math.abs(currZ - toZ)) > newViewDistance) {
this.removeCallback(parameter, currX, currZ);
}
}
@@ -136,7 +134,7 @@ public abstract class SingleUserAreaMap<T> {
for (int currZ = newMinZ; currZ <= newMaxZ; ++currZ) {
// only add if we're outside the old view distance...
if (Math.max(IntegerUtil.branchlessAbs(currX - fromX), IntegerUtil.branchlessAbs(currZ - fromZ)) > oldViewDistance) {
if (Math.max(Math.abs(currX - fromX), Math.abs(currZ - fromZ)) > oldViewDistance) {
this.addCallback(parameter, currX, currZ);
}
}

View File

@@ -0,0 +1,176 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import java.util.List;
import java.util.function.Consumer;
public abstract class BaseChunkSystemHooks implements ChunkSystemHooks {
@Override
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
this.scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
}
@Override
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkTask(chunkX, chunkZ, run, priority);
}
@Override
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
final Consumer<ChunkAccess> onComplete) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(chunkX, chunkZ, gen, toStatus, addTicket, priority, onComplete);
}
@Override
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}
@Override
public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
final FullChunkStatus toStatus, final boolean addTicket,
final Priority priority, final Consumer<LevelChunk> onComplete) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}
@Override
public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders();
}
@Override
public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders();
}
@Override
public int getVisibleChunkHolderCount(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.size();
}
@Override
public int getUpdatingChunkHolderCount(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.size();
}
@Override
public boolean hasAnyChunkHolders(final ServerLevel level) {
return this.getUpdatingChunkHolderCount(level) != 0;
}
@Override
public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
}
@Override
public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
// Update progress listener for LevelLoadingScreen
final ChunkProgressListener progressListener = level.getChunkSource().chunkMap.progressListener;
if (progressListener != null) {
this.scheduleChunkTask(level, holder.getPos().x, holder.getPos().z, () -> {
progressListener.onStatusChange(holder.getPos(), null);
});
}
}
@Override
public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
.moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, chunk);
}
@Override
public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().add(chunk);
}
@Override
public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().remove(chunk);
}
@Override
public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
.moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, null);
}
@Override
public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().add(chunk);
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
}
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
}
@Override
public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(chunk);
}
@Override
public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().add(chunk);
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$markChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
}
@Override
public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().remove(chunk);
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$removeChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
}
@Override
public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
return null;
}
@Override
public int getSendViewDistance(final ServerPlayer player) {
return RegionizedPlayerChunkLoader.getAPISendViewDistance(player);
}
@Override
public int getViewDistance(final ServerPlayer player) {
return RegionizedPlayerChunkLoader.getAPIViewDistance(player);
}
@Override
public int getTickViewDistance(final ServerPlayer player) {
return RegionizedPlayerChunkLoader.getAPITickViewDistance(player);
}
@Override
public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player) {
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().addPlayer(player);
}
@Override
public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player) {
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().removePlayer(player);
}
@Override
public void updateMaps(final ServerLevel world, final ServerPlayer player) {
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().updatePlayer(player);
}
}

View File

@@ -1,176 +0,0 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import com.mojang.logging.LogUtils;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.slf4j.Logger;
import java.util.List;
import java.util.function.Consumer;
public final class ChunkSystem {
private static final Logger LOGGER = LogUtils.getLogger();
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
}
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkTask(chunkX, chunkZ, run, priority);
}
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
final Consumer<ChunkAccess> onComplete) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(chunkX, chunkZ, gen, toStatus, addTicket, priority, onComplete);
}
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}
public static void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
final FullChunkStatus toStatus, final boolean addTicket,
final Priority priority, final Consumer<LevelChunk> onComplete) {
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}
public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders();
}
public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.getOldChunkHolders();
}
public static int getVisibleChunkHolderCount(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.size();
}
public static int getUpdatingChunkHolderCount(final ServerLevel level) {
return ((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().chunkHolderManager.size();
}
public static boolean hasAnyChunkHolders(final ServerLevel level) {
return getUpdatingChunkHolderCount(level) != 0;
}
public static boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event) {
if (!PlatformHooks.get().screenEntity(level, entity, fromDisk, event)) {
return false;
}
return true;
}
public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
}
public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
// Update progress listener for LevelLoadingScreen
final ChunkProgressListener progressListener = level.getChunkSource().chunkMap.progressListener;
if (progressListener != null) {
ChunkSystem.scheduleChunkTask(level, holder.getPos().x, holder.getPos().z, () -> {
progressListener.onStatusChange(holder.getPos(), null);
});
}
}
public static void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
.moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, chunk);
}
public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().add(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
}
public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().remove(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
}
public static void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
.moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, null);
}
public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().add(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
}
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$markChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
}
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$removeChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
}
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().add(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
}
public static void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getEntityTickingChunks().remove(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
}
public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
return null;
}
public static int getSendViewDistance(final ServerPlayer player) {
return RegionizedPlayerChunkLoader.getAPISendViewDistance(player);
}
public static int getViewDistance(final ServerPlayer player) {
return RegionizedPlayerChunkLoader.getAPIViewDistance(player);
}
public static int getTickViewDistance(final ServerPlayer player) {
return RegionizedPlayerChunkLoader.getAPITickViewDistance(player);
}
public static void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player) {
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().addPlayer(player);
}
public static void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player) {
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().removePlayer(player);
}
public static void updateMaps(final ServerLevel world, final ServerPlayer player) {
((ChunkSystemServerLevel)world).moonrise$getPlayerChunkLoader().updatePlayer(player);
}
private ChunkSystem() {}
}

View File

@@ -0,0 +1,80 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.concurrentutil.util.Priority;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import java.util.List;
import java.util.function.Consumer;
public interface ChunkSystemHooks {
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run);
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority);
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
final Consumer<ChunkAccess> onComplete);
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete);
public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
final FullChunkStatus toStatus, final boolean addTicket,
final Priority priority, final Consumer<LevelChunk> onComplete);
public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level);
public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level);
public int getVisibleChunkHolderCount(final ServerLevel level);
public int getUpdatingChunkHolderCount(final ServerLevel level);
public boolean hasAnyChunkHolders(final ServerLevel level);
public boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event);
public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder);
public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder);
public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder);
public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ);
public int getSendViewDistance(final ServerPlayer player);
public int getViewDistance(final ServerPlayer player);
public int getTickViewDistance(final ServerPlayer player);
public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player);
public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player);
public void updateMaps(final ServerLevel world, final ServerPlayer player);
public long[] getCounterTypesUncached(final TicketType type);
}

View File

@@ -16,7 +16,8 @@ public final class CoordinateUtils {
}
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) {

View File

@@ -0,0 +1,44 @@
package ca.spottedleaf.moonrise.common.util;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public final class EntityUtil {
private static final ThreadLocal<DecimalFormat> THREE_DECIMAL_PLACES = ThreadLocal.withInitial(() -> {
return new DecimalFormat("#,##0.000");
});
private static String formatVec(final Vec3 vec) {
final DecimalFormat format = THREE_DECIMAL_PLACES.get();
return "(" + format.format(vec.x) + "," + format.format(vec.y) + "," + format.format(vec.z) + ")";
}
private static String dumpEntityWithoutReferences(final Entity entity) {
if (entity == null) {
return "{null}";
}
return "{type=" + entity.getClass().getSimpleName() + ",id=" + entity.getId() + ",uuid=" + entity.getUUID() + ",pos=" + formatVec(entity.position())
+ ",mot=" + formatVec(entity.getDeltaMovement()) + ",aabb=" + entity.getBoundingBox() + ",removed=" + entity.getRemovalReason() + ",has_vehicle=" + (entity.getVehicle() != null)
+ ",passenger_count=" + entity.getPassengers().size();
}
public static String dumpEntity(final Entity entity) {
final List<Entity> passengers = entity.getPassengers();
final List<String> passengerStrings = new ArrayList<>(passengers.size());
for (final Entity passenger : passengers) {
passengerStrings.add("(" + dumpEntityWithoutReferences(passenger) + ")");
}
return "{root=[" + dumpEntityWithoutReferences(entity) + "], vehicle=[" + dumpEntityWithoutReferences(entity.getVehicle())
+ "], passengers=[" + String.join(",", passengerStrings) + "]";
}
private EntityUtil() {}
}

View File

@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.common.util;
import com.google.gson.JsonElement;
import com.google.gson.Strictness;
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter;
import java.io.File;
@@ -16,7 +17,7 @@ public final class JsonUtil {
final StringWriter stringWriter = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(" ");
jsonWriter.setLenient(false);
jsonWriter.setStrictness(Strictness.LENIENT);
Streams.write(element, jsonWriter);
final String jsonString = stringWriter.toString();

View File

@@ -15,56 +15,81 @@ public class TickThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
private static String getThreadContext() {
return "thread=" + Thread.currentThread().getName();
}
/**
* @deprecated
*/
@Deprecated
public static void ensureTickThread(final String reason) {
if (!isTickThread()) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
LOGGER.error("Thread failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
if (!isTickThreadFor(world, pos, blockRadius)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
}
@@ -105,6 +130,10 @@ public class TickThread extends Thread {
return isTickThread();
}
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
return isTickThread();
}
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
return isTickThread();
}

View File

@@ -87,7 +87,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
if (doTick && this.shouldTickBlocksAt(tileEntity.getPos())) {
tileEntity.tick();
// call mid tick tasks for chunk system
// call mid-tick tasks for chunk system
if ((++tickedEntities & 7) == 0) {
((ChunkSystemLevel)(Level)(Object)this).moonrise$midTickTasks();
}

View File

@@ -125,7 +125,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
* @author Spottedleaf
*/
@Overwrite
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
public <T extends Comparable<T>> T getNullableValue(final Property<T> property) {
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
}
@@ -166,7 +166,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
*/
@Overwrite
public Map<Property<?>, Comparable<?>> getValues() {
ZeroCollidingReferenceStateTable<O, S> table = this.optimisedTable;
final ZeroCollidingReferenceStateTable<O, S> table = this.optimisedTable;
// We have to use this.values until the table is loaded
return table.isLoaded() ? table.getMapView(this.tableIndex) : this.values;
}

View File

@@ -366,7 +366,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
/**
* @reason Use ticket system to control ticket levels
* @author Spottedleaf
* @see net.minecraft.server.level.ServerChunkCache#addRegionTicket(TicketType, ChunkPos, int, Object)
* @see net.minecraft.server.level.ServerChunkCache#addTicketWithRadius(TicketType, ChunkPos, int)
*/
@Overwrite
public void setTicketLevel(int i) {
@@ -397,8 +397,14 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
* @reason Chunk system hooks for ticket level updating now in {@link NewChunkHolder#processTicketLevelUpdate(List, List)}
* @author Spottedleaf
*/
@Overwrite
public void updateFutures(final ChunkMap chunkMap, final Executor executor) {
// inject to avoid conflicting with fabric API's mixin here, we call their event in FabricHooks
@Inject(
method = "updateFutures",
at = @At(
value = "HEAD"
)
)
public void clobberUpdateFutures(final ChunkMap chunkMap, final Executor executor, final CallbackInfo ci) {
throw new UnsupportedOperationException();
}

View File

@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.TicketStorage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@@ -15,8 +16,8 @@ abstract class ChunkMap$DistanceManagerMixin extends net.minecraft.server.level.
@Final
ChunkMap field_17443;
protected ChunkMap$DistanceManagerMixin(Executor executor, Executor executor2) {
super(executor, executor2);
protected ChunkMap$DistanceManagerMixin(final TicketStorage p_394060_, final Executor p_140774_, final Executor p_140775_) {
super(p_394060_, p_140774_, p_140775_);
}
/**

View File

@@ -1,7 +1,7 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemChunkMap;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
@@ -32,6 +32,7 @@ import net.minecraft.util.Mth;
import net.minecraft.util.StaticCache2D;
import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.TicketStorage;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.LevelChunk;
@@ -43,7 +44,6 @@ import net.minecraft.world.level.chunk.storage.IOWorker;
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
import net.minecraft.world.level.entity.ChunkStatusUpdateListener;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
@@ -124,11 +124,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
)
private void constructor(
ServerLevel arg, LevelStorageSource.LevelStorageAccess arg2, DataFixer dataFixer,
StructureTemplateManager arg3, Executor executor, BlockableEventLoop<Runnable> arg4,
LightChunkGetter arg5, ChunkGenerator arg6, ChunkProgressListener arg7,
ChunkStatusUpdateListener arg8, Supplier<DimensionDataStorage> supplier, int j, boolean bl,
final CallbackInfo ci) {
ServerLevel p_214836_, LevelStorageSource.LevelStorageAccess p_214837_, DataFixer p_214838_,
StructureTemplateManager p_214839_, Executor p_214840_, BlockableEventLoop p_214841_,
LightChunkGetter p_214842_, ChunkGenerator p_214843_, ChunkProgressListener p_214844_,
ChunkStatusUpdateListener p_214845_, Supplier p_214846_, TicketStorage p_394462_, int p_214847_,
boolean p_214848_, CallbackInfo ci) {
// intentionally destroy old chunk system hooks
this.updatingChunkMap = null;
this.visibleChunkMap = null;
@@ -143,7 +143,8 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
// Dummy impl for mods that try to loadAsync directly
this.worker = new IOWorker(
// copied from super call
new RegionStorageInfo(arg2.getLevelId(), arg.dimension(), "chunk"), arg2.getDimensionPath(arg.dimension()).resolve("region"), bl
new RegionStorageInfo(p_214837_.getLevelId(), p_214836_.dimension(), "chunk"),
p_214837_.getDimensionPath(p_214836_.dimension()).resolve("region"), p_214848_
) {
@Override
public boolean isOldChunkAround(final ChunkPos chunkPos, final int i) {
@@ -499,7 +500,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
*/
@Overwrite
public int getPlayerViewDistance(final ServerPlayer player) {
return ChunkSystem.getSendViewDistance(player);
return PlatformHooks.get().getSendViewDistance(player);
}
/**
@@ -543,13 +544,31 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @author Spottedleaf
*/
@Redirect(
method = "forEachSpawnCandidateChunk",
method = "collectSpawningChunks",
at = @At(
value = "INVOKE",
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
)
)
private <V> V redirectChunkHolderGet(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
private <V> V redirectChunkHolderGetForSpawning(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
return (V)this.getVisibleChunkIfPresent(key);
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Redirect(
method = {
"method_67499",
"lambda$forEachBlockTickingChunk$36"
},
at = @At(
value = "INVOKE",
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
)
)
private <V> V redirectChunkHolderGetForBlockTicking(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
return (V)this.getVisibleChunkIfPresent(key);
}
@@ -597,7 +616,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
)
private void avoidUpdateChunkTrackingInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer) {
ChunkSystem.addPlayerToDistanceMaps(this.level, serverPlayer);
PlatformHooks.get().addPlayerToDistanceMaps(this.level, serverPlayer);
}
/**
@@ -627,7 +646,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
private void avoidApplyChunkTrackingViewInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer,
final ChunkTrackingView chunkTrackingView) {
ChunkSystem.removePlayerFromDistanceMaps(this.level, serverPlayer);
PlatformHooks.get().removePlayerFromDistanceMaps(this.level, serverPlayer);
}
/**
@@ -641,7 +660,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
)
private void updateMapsHook(final ServerPlayer player, final CallbackInfo ci) {
ChunkSystem.updateMaps(this.level, player);
PlatformHooks.get().updateMaps(this.level, player);
}
/**

View File

@@ -1,22 +1,27 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketStorage;
import it.unimi.dsi.fastutil.longs.LongConsumer;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.LoadingChunkTracker;
import net.minecraft.server.level.SimulationChunkTracker;
import net.minecraft.server.level.ThrottlingChunkTaskDispatcher;
import net.minecraft.server.level.Ticket;
import net.minecraft.server.level.TicketType;
import net.minecraft.server.level.TickingTracker;
import net.minecraft.util.Mth;
import net.minecraft.util.SortedArraySet;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.TicketStorage;
import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@@ -24,6 +29,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -31,13 +37,14 @@ import java.util.concurrent.Executor;
abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
@Shadow
Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets;
public LoadingChunkTracker loadingChunkTracker;
@Shadow
private DistanceManager.ChunkTicketTracker ticketTracker;
public SimulationChunkTracker simulationChunkTracker;
@Shadow
private TickingTracker tickingTicketsTracker;
@Final
private TicketStorage ticketStorage;
@Shadow
private DistanceManager.PlayerTicketTracker playerTicketManager;
@@ -64,7 +71,8 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
}
/**
* @reason Destroy old chunk system state to prevent it from being used
* @reason Destroy old chunk system state to prevent it from being used, and set the chunk map
* for the ticket storage
* @author Spottedleaf
*/
@Inject(
@@ -74,15 +82,16 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
)
)
private void destroyFields(final CallbackInfo ci) {
this.tickets = null;
this.ticketTracker = null;
this.tickingTicketsTracker = null;
this.loadingChunkTracker = null;
this.simulationChunkTracker = null;
this.playerTicketManager = null;
this.chunksToUpdateFutures = null;
this.ticketDispatcher = null;
this.ticketsToRelease = null;
this.mainThreadExecutor = null;
this.simulationDistance = -1;
((ChunkSystemTicketStorage)this.ticketStorage).moonrise$setChunkMap(this.moonrise$getChunkMap());
}
@Override
@@ -90,15 +99,6 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public void purgeStaleTickets() {
this.moonrise$getChunkHolderManager().tick();
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
@@ -108,46 +108,6 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
return this.moonrise$getChunkHolderManager().processTicketUpdates();
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public void addTicket(final long pos, final Ticket<?> ticket) {
this.moonrise$getChunkHolderManager().addTicketAtLevel((TicketType)ticket.getType(), pos, ticket.getTicketLevel(), ticket.key);
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public void removeTicket(final long pos, final Ticket<?> ticket) {
this.moonrise$getChunkHolderManager().removeTicketAtLevel((TicketType)ticket.getType(), pos, ticket.getTicketLevel(), ticket.key);
}
/**
* @reason Remove old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public SortedArraySet<Ticket<?>> getTickets(final long pos) {
throw new UnsupportedOperationException();
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public void updateChunkForced(final ChunkPos pos, final boolean forced) {
if (forced) {
this.moonrise$getChunkHolderManager().addTicketAtLevel(TicketType.FORCED, pos, ChunkMap.FORCED_TICKET_LEVEL, pos);
} else {
this.moonrise$getChunkHolderManager().removeTicketAtLevel(TicketType.FORCED, pos, ChunkMap.FORCED_TICKET_LEVEL, pos);
}
}
/**
* @reason Remove old chunk system hooks
* @author Spottedleaf
@@ -170,11 +130,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
method = "addPlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/TickingTracker;addTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"
target = "Lnet/minecraft/world/level/TicketStorage;addTicket(Lnet/minecraft/server/level/Ticket;Lnet/minecraft/world/level/ChunkPos;)V"
)
)
private <T> void skipTickingTicketTrackerAdd(final TickingTracker instance, final TicketType<T> ticketType,
final ChunkPos chunkPos, final int i, final T object) {}
private void skipTickingTicketTrackerAdd(final TicketStorage instance, final Ticket ticket, final ChunkPos pos) {}
/**
* @reason Remove old chunk system hooks
@@ -213,11 +172,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
method = "removePlayer",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/TickingTracker;removeTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"
target = "Lnet/minecraft/world/level/TicketStorage;removeTicket(Lnet/minecraft/server/level/Ticket;Lnet/minecraft/world/level/ChunkPos;)V"
)
)
private <T> void skipTickingTicketTrackerRemove(final TickingTracker instance, final TicketType<T> ticketType,
final ChunkPos chunkPos, final int i, final T object) {}
private void skipTickingTicketTrackerRemove(final TicketStorage instance, final Ticket ticket, final ChunkPos pos) {}
/**
* @reason Remove old chunk system hooks
@@ -268,8 +226,9 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
* @author Spottedleaf
*/
@Overwrite
public String getTicketDebugString(final long pos) {
return this.moonrise$getChunkHolderManager().getTicketDebugString(pos);
public int getChunkLevel(final long pos, final boolean simulation) {
final NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(pos);
return chunkHolder == null ? ChunkHolderManager.MAX_TICKET_LEVEL + 1 : chunkHolder.getTicketLevel();
}
/**
@@ -293,55 +252,30 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(clamped);
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public void forEachEntityTickingChunk(final LongConsumer consumer) {
final ReferenceList<LevelChunk> chunks = ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getEntityTickingChunks();
final LevelChunk[] raw = chunks.getRawDataUnchecked();
final int size = chunks.size();
Objects.checkFromToIndex(0, size, raw.length);
for (int i = 0; i < size; ++i) {
final LevelChunk chunk = raw[i];
consumer.accept(CoordinateUtils.getChunkKey(chunk.getPos()));
}
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public String getDebugStatus() {
return "No DistanceManager stats available";
}
/**
* @reason Remove old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void dumpTickets(final String file) {
throw new UnsupportedOperationException();
}
/**
* @reason Remove old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public TickingTracker tickingTracker() {
throw new UnsupportedOperationException();
}
/**
* @reason Remove old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public LongSet getTickingChunks() {
throw new UnsupportedOperationException();
}
/**
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
* @author Spottedleaf
*/
@Overwrite
public void removeTicketsOnClosing() {}
/**
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
* @author Spottedleaf
*/
@Overwrite
public boolean hasTickets() {
throw new UnsupportedOperationException();
return "N/A";
}
}

View File

@@ -0,0 +1,30 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(DynamicGameEventListener.class)
abstract class DynamicGameEventListenerMixin {
@Shadow
@Nullable
private SectionPos lastSection;
@Inject(
method = "remove",
at = @At("RETURN")
)
private void onRemove(final CallbackInfo ci) {
// We need to unset the last section when removed, otherwise if the same instance is re-added at the same position it
// will assume there was no change and fail to re-register.
// In vanilla, chunks rarely unload and re-load quickly enough to trigger this issue. However, our chunk system has a
// quirk where fast chunk reload cycles will often occur on player login (see PR #22).
// So we fix this vanilla oversight as our changes cause it to manifest in bugs much more often (see issue #87).
this.lastSection = null;
}
}

View File

@@ -4,6 +4,7 @@ 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.level.ChunkSystemLevel;
import com.google.common.collect.ImmutableList;
import com.mojang.logging.LogUtils;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
@@ -32,10 +33,6 @@ abstract class EntityMixin implements ChunkSystemEntity {
@Shadow
protected abstract Stream<Entity> getIndirectPassengersStream();
@Shadow
@Final
private static Logger LOGGER;
@Shadow
private Level level;
@@ -43,6 +40,8 @@ abstract class EntityMixin implements ChunkSystemEntity {
@Nullable
private Entity.RemovalReason removalReason;
@Unique
private static final Logger LOGGER = LogUtils.getLogger();
@Unique
private final boolean isHardColliding = this.moonrise$isHardCollidingUncached();

View File

@@ -25,7 +25,7 @@ abstract class EntityTickListMixin {
private Int2ObjectMap<Entity> passive;
@Unique
private final IteratorSafeOrderedReferenceSet<Entity> entities = new IteratorSafeOrderedReferenceSet<>();
private final IteratorSafeOrderedReferenceSet<Entity> entities = new IteratorSafeOrderedReferenceSet<>(Entity.class);
/**
* @reason Initialise new fields and destroy old state

View File

@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.ticks.ChunkSystemLevelChunkTicks;
import net.minecraft.core.Registry;
import net.minecraft.server.level.ServerChunkCache;
@@ -48,7 +49,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
private boolean postProcessingDone;
@Unique
private ServerChunkCache.ChunkAndHolder chunkAndHolder;
private NewChunkHolder chunkAndHolder;
@Override
public final boolean moonrise$isPostProcessingDone() {
@@ -56,12 +57,12 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
}
@Override
public final ServerChunkCache.ChunkAndHolder moonrise$getChunkAndHolder() {
public final NewChunkHolder moonrise$getChunkHolder() {
return this.chunkAndHolder;
}
@Override
public final void moonrise$setChunkAndHolder(final ServerChunkCache.ChunkAndHolder holder) {
public final void moonrise$setChunkHolder(final NewChunkHolder holder) {
this.chunkAndHolder = holder;
}

View File

@@ -105,12 +105,12 @@ abstract class LevelChunkTicksMixin<T> implements ChunkSystemLevelChunkTicks, Se
* @author Spottedleaf
*/
@Inject(
method = "save(JLjava/util/function/Function;)Lnet/minecraft/nbt/ListTag;",
method = "pack",
at = @At(
value = "HEAD"
)
)
private void saveHook(final long time, final Function<T, String> idFunction, final CallbackInfoReturnable<ListTag> cir) {
private void saveHook(final long time, final CallbackInfoReturnable<ListTag> cir) {
this.lastSaved = time;
}

View File

@@ -190,6 +190,11 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
)
)
private boolean doNotWaitChunkSystemShutdown(final Stream<ServerLevel> instance, final Predicate<? super ServerLevel> predicate) {
// note: make sure we call deactivateTicketsOnClosing
for (final ServerLevel world : this.getAllLevels()) {
world.getChunkSource().deactivateTicketsOnClosing();
}
return false;
}
@@ -238,9 +243,9 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
)
)
private void closeIOThreads(final CallbackInfo ci) {
LOGGER.info("Waiting for I/O tasks to complete...");
LOGGER.info("Waiting for all RegionFile I/O tasks to complete...");
MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
LOGGER.info("All I/O tasks to complete");
LOGGER.info("All RegionFile I/O tasks to complete");
if ((Object)this instanceof DedicatedServer) {
MoonriseCommon.haltExecutors();
}

View File

@@ -3,18 +3,17 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer;
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.ChunkResult;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
@@ -35,8 +34,6 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@@ -59,9 +56,6 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
@Unique
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
@Unique
private long chunksTicked;
@Override
public final void moonrise$setFullChunk(final int chunkX, final int chunkZ, final LevelChunk chunk) {
final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
@@ -86,6 +80,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
completable::complete
);
if (!completable.isDone() && chunkTaskScheduler.hasShutdown()) {
throw new IllegalStateException(
"Chunk system has shut down, cannot process chunk requests in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.level) + "' at "
+ "(" + chunkX + "," + chunkZ + ") status: " + toStatus
);
}
if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
this.mainThreadProcessor.managedBlock(completable::isDone);
@@ -332,38 +333,34 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
}
/**
* @reason Perform mid-tick chunk task processing during chunk tick
* @author Spottedleaf
*/
@Inject(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
at = @At(
value = "INVOKE",
shift = At.Shift.AFTER,
target = "Lnet/minecraft/server/level/ServerLevel;tickChunk(Lnet/minecraft/world/level/chunk/LevelChunk;I)V"
)
)
private void midTickChunks(final CallbackInfo ci) {
if ((++this.chunksTicked & 7L) != 0L) {
return;
}
((ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
}
/**
* @reason In the chunk system, ticking chunks always have loaded entities. Of course, they are also always
* marked to be as ticking as well.
* @reason In the chunk system, spawn chunks will return only entity ticking chunks - we can elide the
* entity ticking range check.
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
method = "tickSpawningChunk",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
target = "Lnet/minecraft/server/level/DistanceManager;inEntityTickingRange(J)Z"
)
)
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
private boolean shortTickThunder(final DistanceManager instance, final long pos) {
return true;
}
/**
* @reason In the chunk system, spawn chunks will return only entity ticking chunks - we can elide the
* entity ticking check.
* @author Spottedleaf
*/
@Redirect(
method = "tickSpawningChunk",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerLevel;canSpawnEntitiesInChunk(Lnet/minecraft/world/level/ChunkPos;)Z"
)
)
private boolean onlyCheckWBForSpawning(final ServerLevel instance, final ChunkPos pos) {
return instance.getWorldBorder().isWithinBounds(pos);
}
}

View File

@@ -10,7 +10,6 @@ import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.EntityData
import ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller.PoiDataController;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
@@ -27,13 +26,11 @@ import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
@@ -64,12 +61,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
@Mixin(ServerLevel.class)
@@ -121,16 +116,16 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
private final NearbyPlayers nearbyPlayers = new NearbyPlayers((ServerLevel)(Object)this);
@Unique
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
private static final LevelChunk[] EMPTY_LEVEL_CHUNKS = new LevelChunk[0];
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
private final ReferenceList<LevelChunk> loadedChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
private final ReferenceList<LevelChunk> tickingChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
private final ReferenceList<LevelChunk> entityTickingChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
/**
* @reason Initialise fields / destroy entity manager state
@@ -334,17 +329,17 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
}
@Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getLoadedChunks() {
public final ReferenceList<LevelChunk> moonrise$getLoadedChunks() {
return this.loadedChunks;
}
@Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks() {
public final ReferenceList<LevelChunk> moonrise$getTickingChunks() {
return this.tickingChunks;
}
@Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks() {
public final ReferenceList<LevelChunk> moonrise$getEntityTickingChunks() {
return this.entityTickingChunks;
}
@@ -612,6 +607,22 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
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
* @author Spottedleaf
@@ -666,7 +677,7 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
* @author Spottedleaf
*/
@Overwrite
public boolean isPositionEntityTicking(BlockPos pos) {
public boolean isPositionEntityTicking(final BlockPos pos) {
final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos));
return chunkHolder != null && chunkHolder.isEntityTickingReady();
}
@@ -676,7 +687,7 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
* @author Spottedleaf
*/
@Overwrite
public boolean isNaturalSpawningAllowed(final BlockPos pos) {
public boolean areEntitiesActuallyLoadedAndTicking(final ChunkPos pos) {
final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos));
return chunkHolder != null && chunkHolder.isEntityTickingReady();
}
@@ -685,8 +696,14 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
* @reason Redirect to chunk system
* @author Spottedleaf
*/
@Overwrite
public boolean isNaturalSpawningAllowed(final ChunkPos pos) {
@Redirect(
method = "canSpawnEntitiesInChunk",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;canPositionTick(Lnet/minecraft/world/level/ChunkPos;)Z"
)
)
private <T extends EntityAccess> boolean redirectCanEntitiesSpawnTickCheck(final PersistentEntitySectionManager<T> instance, final ChunkPos pos) {
final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos));
return chunkHolder != null && chunkHolder.isEntityTickingReady();
}

View File

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

View File

@@ -49,8 +49,7 @@ abstract class SortedArraySetMixin<T> extends AbstractSet<T> implements ChunkSys
if (i >= len) {
return false;
}
if (!filter.test(backingArray[i])) {
++i;
if (!filter.test(backingArray[i++])) {
continue;
}
break;
@@ -58,7 +57,7 @@ abstract class SortedArraySetMixin<T> extends AbstractSet<T> implements ChunkSys
// we only want to write back to backingArray if we really need to
int lastIndex = i; // this is where new elements are shifted to
int lastIndex = i - 1; // this is where new elements are shifted to
for (; i < len; ++i) {
final T curr = backingArray[i];

View File

@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import net.minecraft.server.level.Ticket;
import net.minecraft.server.level.TicketType;
import org.spongepowered.asm.mixin.Final;
@@ -8,61 +9,75 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import java.util.Comparator;
@Mixin(Ticket.class)
abstract class TicketMixin<T> implements ChunkSystemTicket<T>, Comparable<Ticket<?>> {
abstract class TicketMixin<T> implements ChunkSystemTicket<T>, Comparable<Ticket> {
@Shadow
@Final
private TicketType<T> type;
private TicketType type;
@Shadow
@Final
private int ticketLevel;
@Shadow
@Final
public T key;
private long ticksLeft;
@Unique
private long removeDelay;
private T identifier;
@Override
public final long moonrise$getRemoveDelay() {
return this.removeDelay;
return this.ticksLeft;
}
@Override
public final void moonrise$setRemoveDelay(final long removeDelay) {
this.removeDelay = removeDelay;
this.ticksLeft = removeDelay;
}
@Override
public final T moonrise$getIdentifier() {
return this.identifier;
}
@Override
public final void moonrise$setIdentifier(final T identifier) {
if ((identifier == null) != (((ChunkSystemTicketType<T>)(Object)this.type).moonrise$getIdentifierComparator() == null)) {
throw new IllegalStateException("Nullability of identifier should match nullability of comparator");
}
this.identifier = identifier;
}
/**
* @reason Change debug to include remove delay
* @reason Change debug to include remove identifier
* @author Spottedleaf
*/
@Overwrite
@Override
public String toString() {
return "Ticket[" + this.type + " " + this.ticketLevel + " (" + this.key + ")] to die in " + this.removeDelay;
return "Ticket[" + this.type + " " + this.ticketLevel + " (" + this.identifier + ")] to die in " + this.ticksLeft;
}
/**
* @reason Remove old chunk system hook
* @author Spottedleaf
*/
@Overwrite
public void setCreatedTick(final long tickCreated) {
throw new UnsupportedOperationException();
}
@Override
public final int compareTo(final Ticket ticket) {
final int levelCompare = Integer.compare(this.ticketLevel, ((TicketMixin<?>)(Object)ticket).ticketLevel);
if (levelCompare != 0) {
return levelCompare;
}
/**
* @reason Remove old chunk system hook
* @author Spottedleaf
*/
@Overwrite
public boolean timedOut(final long currentTick) {
throw new UnsupportedOperationException();
final int typeCompare = Long.compare(
((ChunkSystemTicketType<T>)(Object)this.type).moonrise$getId(),
((ChunkSystemTicketType<?>)(Object)((TicketMixin<?>)(Object)ticket).type).moonrise$getId()
);
if (typeCompare != 0) {
return typeCompare;
}
final Comparator<T> comparator = ((ChunkSystemTicketType<T>)(Object)this.type).moonrise$getIdentifierComparator();
return comparator == null ? 0 : comparator.compare(this.identifier, ((TicketMixin<T>)(Object)ticket).identifier);
}
}

View File

@@ -0,0 +1,261 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketStorage;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.Ticket;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.SortedArraySet;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.TicketStorage;
import net.minecraft.world.level.saveddata.SavedData;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
@Mixin(TicketStorage.class)
abstract class TicketStorageMixin extends SavedData implements ChunkSystemTicketStorage {
@Shadow
@Final
private Long2ObjectOpenHashMap<List<Ticket>> deactivatedTickets;
@Shadow
private Long2ObjectOpenHashMap<List<Ticket>> tickets;
@Shadow
private LongSet chunksWithForcedTickets;
@Unique
private ChunkMap chunkMap;
@Override
public final ChunkMap moonrise$getChunkMap() {
return this.chunkMap;
}
@Override
public final void moonrise$setChunkMap(final ChunkMap chunkMap) {
this.chunkMap = chunkMap;
}
/**
* @reason Destroy old chunk system state
* @author Spottedleaf
*/
@Inject(
method = "<init>(Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V",
at = @At(
value = "RETURN"
)
)
private void destroyFields(Long2ObjectOpenHashMap p_393873_, Long2ObjectOpenHashMap p_394615_, CallbackInfo ci) {
if (!this.tickets.isEmpty()) {
throw new IllegalStateException("Expect tickets to be empty here!");
}
this.tickets = null;
this.chunksWithForcedTickets = null;
}
/**
* @reason The forced chunks would be empty, as tickets should always be empty.
* We need to do this to avoid throwing immediately.
* @author Spottedleaf
*/
@Redirect(
method = "<init>(Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/TicketStorage;updateForcedChunks()V"
)
)
private void avoidUpdatingForcedChunks(final TicketStorage instance) {}
/**
* @reason Redirect regular ticket retrieval to new chunk system
* @author Spottedleaf
*/
@Redirect(
method = "forEachTicket(Ljava/util/function/BiConsumer;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/TicketStorage;forEachTicket(Ljava/util/function/BiConsumer;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V",
ordinal = 0
)
)
private void redirectRegularTickets(final BiConsumer<ChunkPos, Ticket> consumer, final Long2ObjectOpenHashMap<List<Ticket>> ticketsParam) {
if (ticketsParam != null) {
throw new IllegalStateException("Bad injection point");
}
final Long2ObjectOpenHashMap<SortedArraySet<Ticket>> tickets = ((ChunkSystemServerLevel)this.chunkMap.level)
.moonrise$getChunkTaskScheduler().chunkHolderManager.getTicketsCopy();
for (final Iterator<Long2ObjectMap.Entry<SortedArraySet<Ticket>>> iterator = tickets.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
final Long2ObjectMap.Entry<SortedArraySet<Ticket>> entry = iterator.next();
final long pos = entry.getLongKey();
final SortedArraySet<Ticket> chunkTickets = entry.getValue();
final ChunkPos chunkPos = new ChunkPos(pos);
for (final Ticket ticket : chunkTickets) {
consumer.accept(chunkPos, ticket);
}
}
}
/**
* @reason Avoid setting old chunk system state
* @author Spottedleaf
*/
@Overwrite
public void setLoadingChunkUpdatedListener(final TicketStorage.ChunkUpdated callback) {}
/**
* @reason Avoid setting old chunk system state
* @author Spottedleaf
*/
@Overwrite
public void setSimulationChunkUpdatedListener(final TicketStorage.ChunkUpdated callback) {}
/**
* @reason Redirect to new chunk system
* @author Spottedleaf
*/
@Overwrite
public boolean hasTickets() {
return ((ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager.hasTickets();
}
/**
* @reason Redirect to new chunk system
* @author Spottedleaf
*/
@Overwrite
public List<Ticket> getTickets(final long pos) {
return ((ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager
.getTicketsAt(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
}
/**
* @reason Redirect to new chunk system
* @author Spottedleaf
*/
@Overwrite
public boolean addTicket(final long pos, final Ticket ticket) {
final boolean ret = ((ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager
.addTicketAtLevel(ticket.getType(), pos, ticket.getTicketLevel(), ((ChunkSystemTicket<?>)ticket).moonrise$getIdentifier());
this.setDirty();
return ret;
}
/**
* @reason Redirect to new chunk system
* @author Spottedleaf
*/
@Overwrite
public boolean removeTicket(final long pos, final Ticket ticket) {
final boolean ret = ((ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager
.removeTicketAtLevel(ticket.getType(), pos, ticket.getTicketLevel(), ((ChunkSystemTicket<?>)ticket).moonrise$getIdentifier());
if (ret) {
this.setDirty();
}
return ret;
}
/**
* @reason Redirect to new chunk system
* @author Spottedleaf
*/
@Overwrite
public void purgeStaleTickets(final ChunkMap chunkMap) {
((ChunkSystemServerLevel)chunkMap.level).moonrise$getChunkTaskScheduler().chunkHolderManager.tick();
this.setDirty();
}
/**
* @reason All tickets (inactive or not) are packed and saved, so there's no real reason we need to remove them.
* Vanilla removes them as it requires every chunk to go through the unload logic; however we already manually
* do this on shutdown.
* @author Spottedleaf
*/
@Redirect(
method = "deactivateTicketsOnClosing",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/TicketStorage;removeTicketIf(Ljava/util/function/BiPredicate;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V"
)
)
private void avoidRemovingTicketsOnShutdown(final TicketStorage instance,
final BiPredicate<Long, Ticket> predicate,
final Long2ObjectOpenHashMap<List<Ticket>> tickets) {}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void removeTicketIf(final BiPredicate<Long, Ticket> predicate, final Long2ObjectOpenHashMap<List<Ticket>> into) {
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void replaceTicketLevelOfType(final int newLevel, final TicketType forType) {
throw new UnsupportedOperationException();
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Overwrite
public LongSet getForceLoadedChunks() {
final Long2IntOpenHashMap forced = ((ChunkSystemServerLevel)this.chunkMap.level).moonrise$getChunkTaskScheduler()
.chunkHolderManager.getTicketCounters(ChunkSystemTicketType.COUNTER_TYPE_FORCED);
if (forced == null) {
return LongSet.of();
}
return forced.keySet();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public LongSet getAllChunksWithTicketThat(final Predicate<Ticket> predicate) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,82 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import net.minecraft.server.level.TicketType;
import org.spongepowered.asm.mixin.Final;
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.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicLong;
@Mixin(TicketType.class)
abstract class TicketTypeMixin<T> implements ChunkSystemTicketType<T> {
@Final
@Shadow
@Mutable
private long timeout;
@Unique
private static AtomicLong ID_GENERATOR;
/**
* @reason Need to initialise at the start of clinit, as ticket types are constructed after.
* Using just the field initialiser would append the static initialiser.
* @author Spottedleaf
*/
@Inject(
method = "<clinit>",
at = @At(
value = "HEAD"
)
)
private static void initIdGenerator(final CallbackInfo ci) {
ID_GENERATOR = new AtomicLong();
}
@Unique
private final long id = ID_GENERATOR.getAndIncrement();
@Unique
private Comparator<T> identifierComparator;
@Unique
private volatile long[] counterTypes;
@Override
public final long moonrise$getId() {
return this.id;
}
@Override
public final Comparator<T> moonrise$getIdentifierComparator() {
return this.identifierComparator;
}
@Override
public final void moonrise$setIdentifierComparator(final Comparator<T> comparator) {
this.identifierComparator = comparator;
}
@Override
public final long[] moonrise$getCounterTypes() {
// need to lazy init this because we cannot check if we are FORCED during construction
final long[] types = this.counterTypes;
if (types != null) {
return types;
}
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

@@ -2,18 +2,27 @@ package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import com.llamalad7.mixinextras.sugar.Local;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -85,11 +94,15 @@ abstract class ChunkMapMixin {
}
/**
* @reason Avoid checking first if there are nearby players, as we make internal perform this implicitly.
* @reason Avoid checking for DEFAULT state, as we make internal perform this implicitly.
* @author Spottedleaf
*/
@Overwrite
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
if (((ChunkTickDistanceManager)this.distanceManager).moonrise$hasAnyNearbyNarrow(pos.x, pos.z)) {
return true;
}
return this.anyPlayerCloseEnoughForSpawningInternal(pos);
}
@@ -152,4 +165,111 @@ abstract class ChunkMapMixin {
return ret == null ? new ArrayList<>() : ret;
}
@Unique
private boolean isChunkNearPlayer(final ChunkMap chunkMap, final ChunkPos chunkPos, final LevelChunk levelChunk) {
final ChunkData chunkData = ((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkHolder().holderData;
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
if (nearbyPlayers == null) {
return false;
}
if (((ChunkTickDistanceManager)this.distanceManager).moonrise$hasAnyNearbyNarrow(chunkPos.x, chunkPos.z)) {
return true;
}
final ReferenceList<ServerPlayer> players = nearbyPlayers.getPlayers(NearbyPlayers.NearbyMapType.SPAWN_RANGE);
if (players == null) {
return false;
}
final ServerPlayer[] raw = players.getRawDataUnchecked();
final int len = players.size();
Objects.checkFromIndexSize(0, len, raw.length);
for (int i = 0; i < len; ++i) {
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
return true;
}
}
return false;
}
/**
* @reason Use the player ticking chunks list, which already contains chunks that are:
* 1. entity ticking
* 2. within spawn range (8 chunks on any axis)
* @author Spottedleaf
*/
@Inject(
method = "collectSpawningChunks",
// use cancellable inject to be compatible with the chunk system's hook here
cancellable = true,
at = @At(
value = "HEAD"
)
)
public void collectSpawningChunks(final List<LevelChunk> list, final CallbackInfo ci) {
final ReferenceList<LevelChunk> tickingChunks = ((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
final Long2IntOpenHashMap forceSpawningChunks = ((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler()
.chunkHolderManager.getTicketCounters(ChunkSystemTicketType.COUNTER_TYPER_NATURAL_SPAWNING_FORCED);
final LevelChunk[] raw = tickingChunks.getRawDataUnchecked();
final int size = tickingChunks.size();
Objects.checkFromToIndex(0, size, raw.length);
if (forceSpawningChunks != null && !forceSpawningChunks.isEmpty()) {
// note: expect forceSpawningChunks.size <<< tickingChunks.size
final LongOpenHashSet seen = new LongOpenHashSet(forceSpawningChunks.size());
final ChunkHolderManager chunkHolderManager = ((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager;
// note: this fixes a bug in neoforge where these chunks don't tick away from a player...
// note: this is NOT the only problem with their implementation, either...
for (final LongIterator iterator = forceSpawningChunks.keySet().longIterator(); iterator.hasNext();) {
final long pos = iterator.nextLong();
final NewChunkHolder holder = chunkHolderManager.getChunkHolder(pos);
if (holder == null || !holder.isEntityTickingReady()) {
continue;
}
seen.add(pos);
list.add((LevelChunk)holder.getCurrentChunk());
}
for (int i = 0; i < size; ++i) {
final LevelChunk levelChunk = raw[i];
if (seen.contains(CoordinateUtils.getChunkKey(levelChunk.getPos()))) {
// do not add duplicate chunks
continue;
}
if (!this.isChunkNearPlayer((ChunkMap)(Object)this, levelChunk.getPos(), levelChunk)) {
continue;
}
list.add(levelChunk);
}
} else {
for (int i = 0; i < size; ++i) {
final LevelChunk levelChunk = raw[i];
if (!this.isChunkNearPlayer((ChunkMap)(Object)this, levelChunk.getPos(), levelChunk)) {
continue;
}
list.add(levelChunk);
}
}
ci.cancel();
}
}

View File

@@ -8,6 +8,7 @@ import it.unimi.dsi.fastutil.longs.LongIterator;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.TriState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@@ -21,20 +22,24 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
@Shadow
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
public DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
@Unique
private final PositionCountingAreaMap<ServerPlayer> spawnChunkTracker = new PositionCountingAreaMap<>();
@Unique
private final PositionCountingAreaMap<ServerPlayer> narrowSpawnChunkTracker = new PositionCountingAreaMap<>();
@Override
public final void moonrise$addPlayer(final ServerPlayer player, final SectionPos pos) {
this.spawnChunkTracker.add(player, pos.x(), pos.z(), ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE);
this.narrowSpawnChunkTracker.add(player, pos.x(), pos.z(), ChunkTickConstants.NARROW_SPAWN_TRACK_RANGE);
}
@Override
public final void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos) {
this.spawnChunkTracker.remove(player);
this.narrowSpawnChunkTracker.remove(player);
}
@Override
@@ -43,11 +48,18 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
final boolean oldIgnore, final boolean newIgnore) {
if (newIgnore) {
this.spawnChunkTracker.remove(player);
this.narrowSpawnChunkTracker.remove(player);
} else {
this.spawnChunkTracker.addOrUpdate(player, newPos.x(), newPos.z(), ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE);
this.narrowSpawnChunkTracker.addOrUpdate(player, newPos.x(), newPos.z(), ChunkTickConstants.NARROW_SPAWN_TRACK_RANGE);
}
}
@Override
public final boolean moonrise$hasAnyNearbyNarrow(final int chunkX, final int chunkZ) {
return this.narrowSpawnChunkTracker.hasObjectsNear(chunkX, chunkZ);
}
/**
* @reason Destroy natural spawning tracker field to prevent it from being used
* @author Spottedleaf
@@ -104,8 +116,11 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
* @author Spottedleaf
*/
@Overwrite
public boolean hasPlayersNearby(final long pos) {
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
public TriState hasPlayersNearby(final long pos) {
if (this.narrowSpawnChunkTracker.hasObjectsNear(pos)) {
return TriState.TRUE;
}
return this.spawnChunkTracker.hasObjectsNear(pos) ? TriState.DEFAULT : TriState.FALSE;
}
/**

View File

@@ -1,30 +1,26 @@
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer;
import net.minecraft.Util;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@Mixin(ServerChunkCache.class)
abstract class ServerChunkCacheMixin extends ChunkSource {
@@ -41,69 +37,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
@Unique
private final SimpleThreadUnsafeRandom shuffleRandom = new SimpleThreadUnsafeRandom(0L);
@Unique
private boolean isChunkNearPlayer(final ChunkMap chunkMap, final ChunkPos chunkPos, final LevelChunk levelChunk) {
final ChunkData chunkData = ((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
.moonrise$getRealChunkHolder().holderData;
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
if (nearbyPlayers == null) {
return false;
}
final ReferenceList<ServerPlayer> players = nearbyPlayers.getPlayers(NearbyPlayers.NearbyMapType.SPAWN_RANGE);
if (players == null) {
return false;
}
final ServerPlayer[] raw = players.getRawDataUnchecked();
final int len = players.size();
Objects.checkFromIndexSize(0, len, raw.length);
for (int i = 0; i < len; ++i) {
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
return true;
}
}
return false;
}
/**
* @reason Use the player ticking chunks list, which already contains chunks that are:
* 1. block ticking
* 2. within spawn range (8 chunks on any axis)
* @author Spottedleaf
*/
@Overwrite
private void collectTickingChunks(final List<LevelChunk> list) {
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
final int size = tickingChunks.size();
final ChunkMap chunkMap = this.chunkMap;
for (int i = 0; i < size; ++i) {
final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i];
final LevelChunk levelChunk = chunkAndHolder.chunk();
if (!this.isChunkNearPlayer(chunkMap, levelChunk.getPos(), levelChunk)) {
continue;
}
list.add(levelChunk);
}
}
/**
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
* function
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks()V",
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;J)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
@@ -113,4 +53,41 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
this.shuffleRandom.setSeed(randomSource.nextLong());
Util.shuffle(list, this.shuffleRandom);
}
/**
* @reason Do not iterate over entire chunk holder map; additionally perform mid-tick chunk task execution
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;J)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ChunkMap;forEachBlockTickingChunk(Ljava/util/function/Consumer;)V"
)
)
private void iterateTickingChunksFaster(final ChunkMap instance, final Consumer<LevelChunk> consumer) {
final ServerLevel world = this.level;
final int randomTickSpeed = world.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
// TODO check on update: impl of forEachBlockTickingChunk will only iterate ENTITY ticking chunks!
// TODO check on update: consumer just runs tickChunk
final ReferenceList<LevelChunk> entityTickingChunks = ((ChunkSystemServerLevel)world).moonrise$getEntityTickingChunks();
// note: we can use the backing array here because:
// 1. we do not care about new additions
// 2. _removes_ are impossible at this stage in the tick
final LevelChunk[] raw = entityTickingChunks.getRawDataUnchecked();
final int size = entityTickingChunks.size();
Objects.checkFromToIndex(0, size, raw.length);
for (int i = 0; i < size; ++i) {
world.tickChunk(raw[i], randomTickSpeed);
// call mid-tick tasks for chunk system
if ((i & 7) == 0) {
((ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
continue;
}
}
}
}

View File

@@ -19,16 +19,16 @@ import org.spongepowered.asm.mixin.Unique;
abstract class ServerLevelMixin implements ChunkTickServerLevel {
@Unique
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_PLAYER_CHUNK_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
private static final LevelChunk[] EMPTY_LEVEL_CHUNKS = new LevelChunk[0];
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> playerTickingChunks = new ReferenceList<>(EMPTY_PLAYER_CHUNK_HOLDERS);
private final ReferenceList<LevelChunk> playerTickingChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
@Unique
private final Long2IntOpenHashMap playerTickingRequests = new Long2IntOpenHashMap();
@Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks() {
public final ReferenceList<LevelChunk> moonrise$getPlayerTickingChunks() {
return this.playerTickingChunks;
}
@@ -39,12 +39,12 @@ abstract class ServerLevelMixin implements ChunkTickServerLevel {
return;
}
this.playerTickingChunks.add(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
this.playerTickingChunks.add(chunk);
}
@Override
public final void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk) {
this.playerTickingChunks.remove(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder());
this.playerTickingChunks.remove(chunk);
}
@Override
@@ -65,9 +65,7 @@ abstract class ServerLevelMixin implements ChunkTickServerLevel {
return;
}
this.playerTickingChunks.add(
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
);
this.playerTickingChunks.add((LevelChunk)chunkHolder.getCurrentChunk());
}
@Override
@@ -93,8 +91,6 @@ abstract class ServerLevelMixin implements ChunkTickServerLevel {
return;
}
this.playerTickingChunks.remove(
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
);
this.playerTickingChunks.remove((LevelChunk)chunkHolder.getCurrentChunk());
}
}

View File

@@ -59,7 +59,7 @@ interface EntityGetterMixin {
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()));
}
}
@@ -74,7 +74,7 @@ interface EntityGetterMixin {
@Overwrite
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
if (voxel.isEmpty()) {
return false;
return true;
}
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();

View File

@@ -55,7 +55,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
public boolean isUnobstructed(final Entity entity) {
final AABB boundingBox = entity.getBoundingBox();
if (CollisionUtil.isEmpty(boundingBox)) {
return false;
return true;
}
final List<Entity> entities = this.getEntities(

View File

@@ -345,11 +345,8 @@ abstract class ShapesMixin {
* @author Spottedleaf
*/
@Overwrite
public static boolean blockOccudes(final VoxelShape first, final VoxelShape second, final Direction direction) {
final boolean firstBlock = first == BLOCK;
final boolean secondBlock = second == BLOCK;
if (firstBlock & secondBlock) {
public static boolean blockOccludes(final VoxelShape first, final VoxelShape second, final Direction direction) {
if (first == BLOCK & second == BLOCK) {
return true;
}

View File

@@ -378,19 +378,19 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
case X: {
final double[] values = this.rootCoordinatesX;
return CollisionUtil.findFloor(
values, value - this.offsetX, 0, values.length - 1
values, this.offsetX, value, 0, values.length - 1
);
}
case Y: {
final double[] values = this.rootCoordinatesY;
return CollisionUtil.findFloor(
values, value - this.offsetY, 0, values.length - 1
values, this.offsetY, value, 0, values.length - 1
);
}
case Z: {
final double[] values = this.rootCoordinatesZ;
return CollisionUtil.findFloor(
values, value - this.offsetZ, 0, values.length - 1
values, this.offsetZ, value, 0, values.length - 1
);
}
default: {
@@ -411,7 +411,7 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
// see findIndex
final int index = CollisionUtil.findFloor(
coords, (positiveDir ? (1.0 - CollisionUtil.COLLISION_EPSILON) : (0.0 + CollisionUtil.COLLISION_EPSILON)) - offset,
coords, offset, (positiveDir ? (1.0 - CollisionUtil.COLLISION_EPSILON) : (0.0 + CollisionUtil.COLLISION_EPSILON)),
0, coords.length - 1
);

View File

@@ -24,8 +24,8 @@ abstract class AcquirePoiMixin {
*/
@Redirect(
method = {
"method_46885",
"*(ZLorg/apache/commons/lang3/mutable/MutableLong;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;Ljava/util/function/Predicate;Lnet/minecraft/world/entity/ai/behavior/declarative/MemoryAccessor;Ljava/util/Optional;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/PathfinderMob;J)Z"
"lambda$create$8",
"method_46885"
},
at = @At(
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;findAllClosestFirstWithType(Ljava/util/function/Predicate;Ljava/util/function/Predicate;Lnet/minecraft/core/BlockPos;ILnet/minecraft/world/entity/ai/village/poi/PoiManager$Occupancy;)Ljava/util/stream/Stream;",
@@ -33,9 +33,9 @@ abstract class AcquirePoiMixin {
ordinal = 0
)
)
private static Stream<Pair<Holder<PoiType>, BlockPos>> aaa(PoiManager poiManager, Predicate<Holder<PoiType>> predicate,
Predicate<BlockPos> predicate2, BlockPos blockPos, int i,
PoiManager.Occupancy occup) {
private static Stream<Pair<Holder<PoiType>, BlockPos>> useLimitedSearch(PoiManager poiManager, Predicate<Holder<PoiType>> predicate,
Predicate<BlockPos> predicate2, BlockPos blockPos, int i,
PoiManager.Occupancy occup) {
final List<Pair<Holder<PoiType>, BlockPos>> ret = new ArrayList<>();
PoiAccess.findNearestPoiPositions(

View File

@@ -1,10 +1,13 @@
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 org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
@Mixin(ServerAddressResolver.class)
@@ -15,24 +18,23 @@ interface ServerAddressResolverMixin {
* @author Spottedleaf
*/
@Redirect(
method = {
"method_36903",
"*(Lnet/minecraft/client/multiplayer/resolver/ServerAddress;)Ljava/util/Optional;"
},
at = @At(
value = "INVOKE",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"
)
method = {
"method_36903",
"lambda$static$0"
},
at = @At(
value = "NEW",
target = "(Ljava/net/InetAddress;I)Ljava/net/InetSocketAddress;"
)
)
private static InetAddress eliminateRDNS(final String name) throws UnknownHostException {
final InetAddress ret = InetAddress.getByName(name);
final byte[] address = ret.getAddress();
private static InetSocketAddress eliminateRDNS(InetAddress addr, final int port,
@Local(ordinal = 0, argsOnly = true) final ServerAddress serverAddress) throws UnknownHostException {
final byte[] address = addr.getAddress();
if (address != null) {
// 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

@@ -49,10 +49,10 @@ import java.util.function.Supplier;
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
@Shadow
private ConsecutiveExecutor consecutiveExecutor;
public ConsecutiveExecutor consecutiveExecutor;
@Shadow
private ChunkTaskDispatcher taskDispatcher;
public ChunkTaskDispatcher taskDispatcher;
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
super(chunkProvider, hasBlockLight, hasSkyLight);
@@ -86,10 +86,10 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
final Long ticketId = Long.valueOf(this.chunkWorkCounter.getAndIncrement());
final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
world.getChunkSource().addRegionTicket(StarLightInterface.CHUNK_WORK_TICKET, pos, StarLightInterface.REGION_LIGHT_TICKET_LEVEL, ticketId);
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(StarLightInterface.CHUNK_WORK_TICKET, pos, StarLightInterface.LIGHT_TICKET_LEVEL, ticketId);
scheduledTask.queueOrRunTask(() -> {
world.getChunkSource().removeRegionTicket(StarLightInterface.CHUNK_WORK_TICKET, pos, StarLightInterface.REGION_LIGHT_TICKET_LEVEL, ticketId);
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(StarLightInterface.CHUNK_WORK_TICKET, pos, StarLightInterface.LIGHT_TICKET_LEVEL, ticketId);
});
}
@@ -105,7 +105,7 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
final ChunkPos pos = iterator.next();
final Long id = ChunkTaskScheduler.getNextChunkRelightId();
world.getChunkSource().addRegionTicket(ChunkTaskScheduler.CHUNK_RELIGHT, pos, StarLightInterface.REGION_LIGHT_TICKET_LEVEL, id);
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.addTicketAtLevel(ChunkTaskScheduler.CHUNK_RELIGHT, pos, StarLightInterface.LIGHT_TICKET_LEVEL, id);
ticketIds.put(pos, id);
final ChunkAccess chunk = (ChunkAccess)world.getChunkSource().getChunkForLighting(pos.x, pos.z);
@@ -113,7 +113,7 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
// cannot relight this chunk
iterator.remove();
ticketIds.remove(pos);
world.getChunkSource().removeRegionTicket(ChunkTaskScheduler.CHUNK_RELIGHT, pos, StarLightInterface.REGION_LIGHT_TICKET_LEVEL, id);
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(ChunkTaskScheduler.CHUNK_RELIGHT, pos, StarLightInterface.LIGHT_TICKET_LEVEL, id);
continue;
}
}
@@ -160,9 +160,9 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
}
for (final Map.Entry<ChunkPos, Long> entry : ticketIds.entrySet()) {
world.getChunkSource().removeRegionTicket(
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(
ChunkTaskScheduler.CHUNK_RELIGHT, entry.getKey(),
StarLightInterface.REGION_LIGHT_TICKET_LEVEL, entry.getValue()
StarLightInterface.LIGHT_TICKET_LEVEL, entry.getValue()
);
}
}

View File

@@ -60,13 +60,13 @@ abstract class SerializableChunkDataMixin {
method = "parse",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/nbt/CompoundTag;getBoolean(Ljava/lang/String;)Z",
target = "Lnet/minecraft/nbt/CompoundTag;getBooleanOr(Ljava/lang/String;Z)Z",
ordinal = 0
)
)
private static boolean setLightCorrect(final CompoundTag instance, final String string,
private static boolean setLightCorrect(final CompoundTag instance, final String string, final boolean dfl,
@Local(ordinal = 0, argsOnly = false) final ChunkStatus status) {
final boolean starlightCorrect = instance.get("isLightOn") != null && instance.getInt(SaveUtil.STARLIGHT_VERSION_TAG) == SaveUtil.STARLIGHT_LIGHT_VERSION;
final boolean starlightCorrect = instance.get("isLightOn") != null && instance.getIntOr(SaveUtil.STARLIGHT_VERSION_TAG, -1) == SaveUtil.STARLIGHT_LIGHT_VERSION;
return status.isOrAfter(ChunkStatus.LIGHT) && starlightCorrect;
}
@@ -84,17 +84,17 @@ abstract class SerializableChunkDataMixin {
)
private static SerializableChunkData.SectionData readStarlightState(final int y, final LevelChunkSection chunkSection,
final DataLayer blockLight, final DataLayer skyLight,
@Local(ordinal = 3, argsOnly = false) final CompoundTag sectionData) {
@Local(ordinal = 2, argsOnly = false) final CompoundTag sectionData) {
final SerializableChunkData.SectionData ret = new SerializableChunkData.SectionData(
y, chunkSection, blockLight, skyLight
);
if (sectionData.contains(SaveUtil.BLOCKLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) {
((StarlightSectionData)(Object)ret).starlight$setBlockLightState(sectionData.getInt(SaveUtil.BLOCKLIGHT_STATE_TAG));
if (sectionData.contains(SaveUtil.BLOCKLIGHT_STATE_TAG)) {
((StarlightSectionData)(Object)ret).starlight$setBlockLightState(sectionData.getIntOr(SaveUtil.BLOCKLIGHT_STATE_TAG, 0));
}
if (sectionData.contains(SaveUtil.SKYLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) {
((StarlightSectionData)(Object)ret).starlight$setSkyLightState(sectionData.getInt(SaveUtil.SKYLIGHT_STATE_TAG));
if (sectionData.contains(SaveUtil.SKYLIGHT_STATE_TAG)) {
((StarlightSectionData)(Object)ret).starlight$setSkyLightState(sectionData.getIntOr(SaveUtil.SKYLIGHT_STATE_TAG, 0));
}
return ret;

View File

@@ -15,12 +15,11 @@ public final class ChunkSystemConverters {
private static final int DEFAULT_ENTITY_CHUNK_DATA_VERSION = -1;
private static int getCurrentVersion() {
return SharedConstants.getCurrentVersion().getDataVersion().getVersion();
return SharedConstants.getCurrentVersion().dataVersion().version();
}
private static int getDataVersion(final CompoundTag data, final int dfl) {
return !data.contains(SharedConstants.DATA_VERSION_TAG, Tag.TAG_ANY_NUMERIC)
? dfl : data.getInt(SharedConstants.DATA_VERSION_TAG);
return data.getIntOr(SharedConstants.DATA_VERSION_TAG, dfl);
}
public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) {

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 net.minecraft.server.level.FullChunkStatus;
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.vehicle.AbstractMinecart;
import net.minecraft.world.entity.vehicle.Boat;
@@ -13,7 +14,7 @@ public interface ChunkSystemEntity {
// for mods to override
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();

View File

@@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO {
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
this.scheduleReadIO();
return;

View File

@@ -48,7 +48,7 @@ public final class EntityDataController extends MoonriseRegionFileIO.RegionDataC
}
private static void checkPosition(final ChunkPos pos, final CompoundTag nbt) {
final ChunkPos nbtPos = nbt == null ? null : EntityStorage.readChunkPos(nbt);
final ChunkPos nbtPos = nbt == null ? null : nbt.read("Position", ChunkPos.CODEC).orElse(null);
if (nbtPos != null && !pos.equals(nbtPos)) {
throw new IllegalArgumentException(
"Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString()

View File

@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.patches.chunk_system.level;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
@@ -10,6 +11,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import java.util.List;
import java.util.function.Consumer;
@@ -56,9 +58,9 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
public NearbyPlayers moonrise$getNearbyPlayers();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getLoadedChunks();
public ReferenceList<LevelChunk> moonrise$getLoadedChunks();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
public ReferenceList<LevelChunk> moonrise$getTickingChunks();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
public ReferenceList<LevelChunk> moonrise$getEntityTickingChunks();
}

View File

@@ -1,13 +1,13 @@
package ca.spottedleaf.moonrise.patches.chunk_system.level.chunk;
import net.minecraft.server.level.ServerChunkCache;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
public interface ChunkSystemLevelChunk {
public boolean moonrise$isPostProcessingDone();
public ServerChunkCache.ChunkAndHolder moonrise$getChunkAndHolder();
public NewChunkHolder moonrise$getChunkHolder();
public void moonrise$setChunkAndHolder(final ServerChunkCache.ChunkAndHolder holder);
public void moonrise$setChunkHolder(final NewChunkHolder holder);
}

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.entity.ChunkSystemEntity;
import com.google.common.collect.ImmutableList;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
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.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason;
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.level.ChunkPos;
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.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 org.slf4j.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -32,6 +39,8 @@ import java.util.function.Predicate;
public final class ChunkEntitySlices {
private static final Logger LOGGER = LogUtils.getLogger();
public final int minSection;
public final int maxSection;
public final int chunkX;
@@ -74,9 +83,12 @@ public final class ChunkEntitySlices {
this.chunkData = chunkData;
}
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
// TODO check this and below on update for format changes
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
public static List<Entity> readEntities(final ServerLevel world, final ChunkPos pos, final CompoundTag compoundTag) {
try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(pos), LOGGER)) {
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
@@ -84,12 +96,12 @@ public final class ChunkEntitySlices {
if (from == null) {
return;
}
final ListTag entitiesFrom = from.getList("Entities", Tag.TAG_COMPOUND);
final ListTag entitiesFrom = from.getListOrEmpty("Entities");
if (entitiesFrom == null || entitiesFrom.isEmpty()) {
return;
}
final ListTag entitiesInto = into.getList("Entities", Tag.TAG_COMPOUND);
final ListTag entitiesInto = into.getListOrEmpty("Entities");
into.put("Entities", entitiesInto); // this is in case into doesn't have any entities
entitiesInto.addAll(0, entitiesFrom);
}
@@ -104,15 +116,25 @@ public final class ChunkEntitySlices {
}
final ListTag entitiesTag = new ListTag();
for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
CompoundTag compoundTag = new CompoundTag();
if (entity.save(compoundTag)) {
entitiesTag.add(compoundTag);
try (final ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(ChunkAccess.problemPath(chunkPos), LOGGER)) {
for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
final TagValueOutput savedEntity = TagValueOutput.createWithContext(
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());
ret.put("Entities", entitiesTag);
EntityStorage.writeChunkPos(ret, chunkPos);
ret.store("Position", ChunkPos.CODEC, chunkPos);
return !force && entitiesTag.isEmpty() ? null : ret;
}
@@ -291,21 +313,12 @@ public final class ChunkEntitySlices {
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
}
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
this.allEntities.getEntities(except, box, into, predicate);
}
public boolean getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
return this.allEntities.getEntitiesWithEnderDragonPartsLimited(except, box, into, predicate, maxCount);
}
public boolean getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
final int maxCount) {
return this.allEntities.getEntitiesLimited(except, box, into, predicate, maxCount);
}
@@ -319,7 +332,7 @@ public final class ChunkEntitySlices {
}
public <T extends Entity> boolean getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate, final int maxCount) {
final Predicate<? super T> predicate, final int maxCount) {
final EntityCollectionBySection byType = this.entitiesByType.get(type);
if (byType != null) {
@@ -356,21 +369,21 @@ public final class ChunkEntitySlices {
final Predicate<? super T> predicate) {
EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
if (collection != null) {
collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
collection.getEntities(except, box, (List)into, (Predicate)predicate);
} else {
this.entitiesByClass.put(clazz, collection = this.initClass(clazz));
collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
collection.getEntities(except, box, (List)into, (Predicate)predicate);
}
}
public <T extends Entity> boolean getEntities(final Class<? extends T> clazz, final Entity except, final AABB box, final List<? super T> into,
final Predicate<? super T> predicate, final int maxCount) {
final Predicate<? super T> predicate, final int maxCount) {
EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
if (collection != null) {
return collection.getEntitiesWithEnderDragonPartsLimited(except, clazz, box, (List)into, (Predicate)predicate, maxCount);
return collection.getEntitiesLimited(except, box, (List)into, (Predicate)predicate, maxCount);
} else {
this.entitiesByClass.put(clazz, collection = this.initClass(clazz));
return collection.getEntitiesWithEnderDragonPartsLimited(except, clazz, box, (List)into, (Predicate)predicate, maxCount);
return collection.getEntitiesLimited(except, box, (List)into, (Predicate)predicate, maxCount);
}
}
@@ -574,225 +587,5 @@ public final class ChunkEntitySlices {
return false;
}
public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
}
final int minSection = this.slices.minSection;
final int maxSection = this.slices.maxSection;
final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
for (int section = min; section <= max; ++section) {
final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
if (list == null) {
continue;
}
final Entity[] storage = list.storage;
for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
final Entity entity = storage[i];
if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
continue;
}
if (predicate == null || predicate.test(entity)) {
into.add(entity);
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
for (final EnderDragonPart part : ((EnderDragon)entity).getSubEntities()) {
if (part == except || !part.getBoundingBox().intersects(box)) {
continue;
}
if (predicate != null && !predicate.test(part)) {
continue;
}
into.add(part);
}
}
}
}
}
public boolean getEntitiesWithEnderDragonPartsLimited(final Entity except, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate, final int maxCount) {
if (this.count == 0) {
return false;
}
final int minSection = this.slices.minSection;
final int maxSection = this.slices.maxSection;
final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
for (int section = min; section <= max; ++section) {
final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
if (list == null) {
continue;
}
final Entity[] storage = list.storage;
for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
final Entity entity = storage[i];
if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
continue;
}
if (predicate == null || predicate.test(entity)) {
into.add(entity);
if (into.size() >= maxCount) {
return true;
}
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
for (final EnderDragonPart part : ((EnderDragon)entity).getSubEntities()) {
if (part == except || !part.getBoundingBox().intersects(box)) {
continue;
}
if (predicate != null && !predicate.test(part)) {
continue;
}
into.add(part);
if (into.size() >= maxCount) {
return true;
}
}
}
}
}
return false;
}
public void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate) {
if (this.count == 0) {
return;
}
final int minSection = this.slices.minSection;
final int maxSection = this.slices.maxSection;
final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
for (int section = min; section <= max; ++section) {
final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
if (list == null) {
continue;
}
final Entity[] storage = list.storage;
for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
final Entity entity = storage[i];
if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
continue;
}
if (predicate == null || predicate.test(entity)) {
into.add(entity);
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
for (final EnderDragonPart part : ((EnderDragon)entity).getSubEntities()) {
if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
continue;
}
if (predicate != null && !predicate.test(part)) {
continue;
}
into.add(part);
}
}
}
}
}
public boolean getEntitiesWithEnderDragonPartsLimited(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
final Predicate<? super Entity> predicate, final int maxCount) {
if (this.count == 0) {
return false;
}
final int minSection = this.slices.minSection;
final int maxSection = this.slices.maxSection;
final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
for (int section = min; section <= max; ++section) {
final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
if (list == null) {
continue;
}
final Entity[] storage = list.storage;
for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
final Entity entity = storage[i];
if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
continue;
}
if (predicate == null || predicate.test(entity)) {
into.add(entity);
if (into.size() >= maxCount) {
return true;
}
} // else: continue to test the ender dragon parts
if (entity instanceof EnderDragon) {
for (final EnderDragonPart part : ((EnderDragon)entity).getSubEntities()) {
if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
continue;
}
if (predicate != null && !predicate.test(part)) {
continue;
}
into.add(part);
if (into.size() >= maxCount) {
return true;
}
}
}
}
}
return false;
}
}
}

View File

@@ -179,6 +179,10 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
}
}
public Iterable<Entity> getAllMapped() {
return this.entityByUUID.values();
}
public int getEntityCount() {
synchronized (this.accessibleEntities) {
return this.accessibleEntities.size();
@@ -209,7 +213,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
@Override
public void get(final AABB box, final Consumer<Entity> action) {
List<Entity> entities = new ArrayList<>();
this.getEntitiesWithoutDragonParts(null, box, entities, null);
this.getEntities((Entity)null, box, entities, null);
for (int i = 0, len = entities.size(); i < len; ++i) {
action.accept(entities.get(i));
}
@@ -218,7 +222,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
@Override
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final AbortableIterationConsumer<U> action) {
List<Entity> entities = new ArrayList<>();
this.getEntitiesWithoutDragonParts(null, box, entities, null);
this.getEntities((Entity)null, box, entities, null);
for (int i = 0, len = entities.size(); i < len; ++i) {
final U casted = filter.tryCast(entities.get(i));
if (casted != null && action.accept(casted).shouldAbort()) {
@@ -560,45 +564,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
return slices;
}
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
final int minRegionX = minChunkX >> REGION_SHIFT;
final int minRegionZ = minChunkZ >> REGION_SHIFT;
final int maxRegionX = maxChunkX >> REGION_SHIFT;
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
if (region == null) {
continue;
}
final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
for (int currZ = minZ; currZ <= maxZ; ++currZ) {
for (int currX = minX; currX <= maxX; ++currX) {
final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) {
continue;
}
chunk.getEntitiesWithoutDragonParts(except, box, into, predicate);
}
}
}
}
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
@@ -759,48 +724,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
//////// Limited ////////
public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
final int minRegionX = minChunkX >> REGION_SHIFT;
final int minRegionZ = minChunkZ >> REGION_SHIFT;
final int maxRegionX = maxChunkX >> REGION_SHIFT;
final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
if (region == null) {
continue;
}
final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
for (int currZ = minZ; currZ <= maxZ; ++currZ) {
for (int currX = minX; currX <= maxX; ++currX) {
final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) {
continue;
}
if (chunk.getEntitiesWithoutDragonParts(except, box, into, predicate, maxCount)) {
return;
}
}
}
}
}
}
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) {
final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;

View File

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

View File

@@ -4,13 +4,17 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
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.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.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.ThrownEnderpearl;
import net.minecraft.world.level.entity.LevelCallback;
public final class ServerEntityLookup extends EntityLookup {
@@ -20,9 +24,18 @@ public final class ServerEntityLookup extends EntityLookup {
private final ServerLevel serverWorld;
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) {
super(world, worldCallback);
this.serverWorld = world;
this.keepEnderPearlsTicking = PlatformHooks.get().addTicketForEnderPearls(world);
}
@Override
@@ -64,6 +77,10 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ServerPlayer 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(
entity,
CoordinateUtils.getChunkSectionKey(oldSectionX, oldSectionY, oldSectionZ),
@@ -76,6 +93,9 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().addPlayer(player);
}
if (entity instanceof ThrownEnderpearl enderpearl) {
this.addEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()));
}
}
@Override
@@ -83,6 +103,9 @@ public final class ServerEntityLookup extends EntityLookup {
if (entity instanceof ServerPlayer player) {
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().removePlayer(player);
}
if (entity instanceof ThrownEnderpearl enderpearl) {
this.removeEnderPearl(CoordinateUtils.getChunkKey(enderpearl.chunkPosition()));
}
}
@Override
@@ -111,6 +134,33 @@ public final class ServerEntityLookup extends EntityLookup {
@Override
protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) {
return ChunkSystem.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();
ret.put("Sections", sections);
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().dataVersion().version());
final ServerLevel world = this.world;
final int chunkX = this.chunkX;
@@ -159,7 +159,7 @@ public final class PoiChunk {
final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, world.registryAccess());
final CompoundTag sections = data.getCompound("Sections");
final CompoundTag sections = data.getCompoundOrEmpty("Sections");
if (sections.isEmpty()) {
// nothing to parse
@@ -176,7 +176,7 @@ public final class PoiChunk {
continue;
}
final CompoundTag section = sections.getCompound(key);
final CompoundTag section = sections.getCompoundOrEmpty(key);
final DataResult<PoiSection.Packed> deserializeResult = PoiSection.Packed.CODEC.parse(registryOps, section);
final int finalSectionY = sectionY;
final PoiSection.Packed packed = deserializeResult.resultOrPartial((final String description) -> {

View File

@@ -14,6 +14,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunk
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
@@ -45,16 +46,17 @@ import java.util.function.Function;
public final class RegionizedPlayerChunkLoader {
public static final TicketType<Long> PLAYER_TICKET = TicketType.create("chunk_system:player_ticket", Long::compareTo);
public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5 * 20);
public static final int MIN_VIEW_DISTANCE = 2;
public static final int MAX_VIEW_DISTANCE = 32;
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, 1L);
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 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 {
private volatile ViewDistances viewDistances;
@@ -149,7 +151,7 @@ public final class RegionizedPlayerChunkLoader {
}
public static int getAPITickViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel();
final ServerLevel level = player.level();
final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (data == null) {
return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPITickDistance();
@@ -158,7 +160,7 @@ public final class RegionizedPlayerChunkLoader {
}
public static int getAPIViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel();
final ServerLevel level = player.level();
final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (data == null) {
return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPIViewDistance();
@@ -168,7 +170,7 @@ public final class RegionizedPlayerChunkLoader {
}
public static int getAPISendViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel();
final ServerLevel level = player.level();
final PlayerChunkLoaderData data = ((ChunkSystemServerPlayer)player).moonrise$getChunkLoader();
if (data == null) {
return ((ChunkSystemServerLevel)level).moonrise$getPlayerChunkLoader().getAPISendViewDistance();
@@ -685,8 +687,7 @@ public final class RegionizedPlayerChunkLoader {
}
this.pushDelayedTicketOp(
ChunkHolderManager.TicketOperation.addOp(
chunk,
PLAYER_TICKET, LOADED_TICKET_LEVEL, this.idBoxed
chunk, PLAYER_TICKET, LOADED_TICKET_LEVEL, this.idBoxed
)
);
chunks.add(chunk);

View File

@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import ca.spottedleaf.concurrentutil.util.Priority;
@@ -7,7 +8,6 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
@@ -17,9 +17,11 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkLoadTas
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkProgressionTask;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ChunkSystemSortedArraySet;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ByteLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
@@ -66,13 +68,12 @@ public final class ChunkHolderManager {
public static final int ENTITY_TICKING_TICKET_LEVEL = ChunkLevel.ENTITY_TICKING_LEVEL;
public static final int MAX_TICKET_LEVEL = ChunkLevel.MAX_LEVEL; // inclusive
public static final TicketType<Unit> UNLOAD_COOLDOWN = TicketType.create("unload_cooldown", (u1, u2) -> 0, 5 * 20);
public static final TicketType UNLOAD_COOLDOWN = ChunkSystemTicketType.create("chunk_system:unload_cooldown", null, 5L * 20L);
private static final long NO_TIMEOUT_MARKER = Long.MIN_VALUE;
private static final long PROBE_MARKER = Long.MIN_VALUE + 1;
public final ReentrantAreaLock ticketLockArea;
private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket<?>>> tickets = new ConcurrentLong2ReferenceChainedHashTable<>();
private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket>> tickets = new ConcurrentLong2ReferenceChainedHashTable<>();
private final ConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>();
final ChunkUnloadQueue unloadQueue;
@@ -82,6 +83,7 @@ public final class ChunkHolderManager {
private long currentTick;
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) -> {
if (c1 == c2) {
return 0;
@@ -103,6 +105,8 @@ public final class ChunkHolderManager {
return Long.compare(coord1, coord2);
});
private final ConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> ticketCounters = new ConcurrentLong2ReferenceChainedHashTable<>();
public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) {
this.world = world;
this.taskScheduler = taskScheduler;
@@ -110,20 +114,20 @@ public final class ChunkHolderManager {
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 ticketMask = (1 << ticketShift) - 1;
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
final boolean ret;
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
((posX >> ticketShift) - 1) << ticketShift,
((posZ >> ticketShift) - 1) << ticketShift,
(((posX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((posZ >> ticketShift) + 1) << ticketShift) | ticketMask
((chunkX >> ticketShift) - 1) << ticketShift,
((chunkZ >> ticketShift) - 1) << ticketShift,
(((chunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((chunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
);
try {
ret = this.processTicketUpdatesNoLock(posX >> ticketShift, posZ >> ticketShift, scheduledTasks, changedFullStatus);
ret = this.processTicketUpdatesNoLock(chunkX >> ticketShift, chunkZ >> ticketShift, scheduledTasks, changedFullStatus);
} finally {
this.ticketLockArea.unlock(ticketLock);
}
@@ -164,7 +168,6 @@ public final class ChunkHolderManager {
return this.chunkHolders.size();
}
// TODO replace the need for this, specifically: optimise ServerChunkCache#tickChunks
public Iterable<ChunkHolder> getOldChunkHoldersIterable() {
return new Iterable<ChunkHolder>() {
@Override
@@ -219,6 +222,8 @@ public final class ChunkHolderManager {
LOGGER.error("Failed to close '" + type.name() + "' regionfile cache for world '" + WorldUtil.getWorldName(this.world) + "'", ex);
}
}
this.taskScheduler.setShutdown(true);
}
void ensureInAutosave(final NewChunkHolder holder) {
@@ -410,7 +415,7 @@ public final class ChunkHolderManager {
public String getTicketDebugString(final long coordinate) {
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(coordinate), CoordinateUtils.getChunkZ(coordinate));
try {
final SortedArraySet<Ticket<?>> tickets = this.tickets.get(coordinate);
final SortedArraySet<Ticket> tickets = this.tickets.get(coordinate);
return tickets != null ? tickets.first().toString() : "no_ticket";
} finally {
@@ -420,8 +425,40 @@ public final class ChunkHolderManager {
}
}
public Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> getTicketsCopy() {
final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> ret = new Long2ObjectOpenHashMap<>();
public boolean hasTickets() {
return !this.tickets.isEmpty();
}
public List<Ticket> getTicketsAt(final int chunkX, final int chunkZ) {
final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
if (!this.tickets.containsKey(key)) {
// avoid contending lock
return new ArrayList<>();
}
final ReentrantAreaLock.Node lock = this.ticketLockArea.lock(chunkX, chunkZ);
try {
final SortedArraySet<Ticket> tickets = this.tickets.get(key);
if (tickets == null) {
return new ArrayList<>();
}
final List<Ticket> ret = new ArrayList<>(tickets.size());
for (final Ticket ticket : tickets) {
ret.add(ticket);
}
return ret;
} finally {
this.ticketLockArea.unlock(lock);
}
}
public Long2ObjectOpenHashMap<SortedArraySet<Ticket>> getTicketsCopy() {
final Long2ObjectOpenHashMap<SortedArraySet<Ticket>> ret = new Long2ObjectOpenHashMap<>();
final Long2ObjectOpenHashMap<LongArrayList> sections = new Long2ObjectOpenHashMap<>();
final int sectionShift = this.taskScheduler.getChunkSystemLockShift();
for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) {
@@ -450,12 +487,12 @@ public final class ChunkHolderManager {
try {
for (final LongIterator iterator2 = coordinates.iterator(); iterator2.hasNext();) {
final long coord = iterator2.nextLong();
final SortedArraySet<Ticket<?>> tickets = this.tickets.get(coord);
final SortedArraySet<Ticket> tickets = this.tickets.get(coord);
if (tickets == null) {
// removed before we acquired lock
continue;
}
ret.put(coord, ((ChunkSystemSortedArraySet<Ticket<?>>)tickets).moonrise$copy());
ret.put(coord, ((ChunkSystemSortedArraySet<Ticket>)tickets).moonrise$copy());
}
} finally {
this.ticketLockArea.unlock(ticketLock);
@@ -473,16 +510,16 @@ public final class ChunkHolderManager {
}
}
private static int getTicketLevelAt(SortedArraySet<Ticket<?>> tickets) {
private static int getTicketLevelAt(final SortedArraySet<Ticket> tickets) {
return !tickets.isEmpty() ? tickets.first().getTicketLevel() : MAX_TICKET_LEVEL + 1;
}
public <T> boolean addTicketAtLevel(final TicketType<T> type, final ChunkPos chunkPos, final int level,
public <T> boolean addTicketAtLevel(final TicketType type, final ChunkPos chunkPos, final int level,
final T identifier) {
return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkPos), level, identifier);
}
public <T> boolean addTicketAtLevel(final TicketType<T> type, final int chunkX, final int chunkZ, final int level,
public <T> boolean addTicketAtLevel(final TicketType type, final int chunkX, final int chunkZ, final int level,
final T identifier) {
return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkX, chunkZ), level, identifier);
}
@@ -523,29 +560,29 @@ public final class ChunkHolderManager {
// supposed to return true if the ticket was added and did not replace another
// but, we always return false if the ticket cannot be added
public <T> boolean addTicketAtLevel(final TicketType<T> type, final long chunk, final int level, final T identifier) {
public <T> boolean addTicketAtLevel(final TicketType type, final long chunk, final int level, final T identifier) {
return this.addTicketAtLevel(type, chunk, level, identifier, true);
}
<T> boolean addTicketAtLevel(final TicketType<T> type, final long chunk, final int level, final T identifier, final boolean lock) {
final long removeDelay = type.timeout <= 0 ? NO_TIMEOUT_MARKER : type.timeout;
<T> boolean addTicketAtLevel(final TicketType type, final long chunk, final int level, final T identifier, final boolean lock) {
final long removeDelay = type.timeout() <= 0 ? NO_TIMEOUT_MARKER : type.timeout();
if (level > MAX_TICKET_LEVEL) {
return false;
}
final int chunkX = CoordinateUtils.getChunkX(chunk);
final int chunkZ = CoordinateUtils.getChunkZ(chunk);
final Ticket<T> ticket = new Ticket<>(type, level, identifier);
((ChunkSystemTicket<T>)(Object)ticket).moonrise$setRemoveDelay(removeDelay);
final Ticket ticket = new Ticket(type, level, removeDelay);
((ChunkSystemTicket<T>)(Object)ticket).moonrise$setIdentifier(identifier);
final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null;
try {
final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> {
return SortedArraySet.create(4);
final SortedArraySet<Ticket> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> {
return (SortedArraySet)SortedArraySet.create(4);
});
final int levelBefore = getTicketLevelAt(ticketsAtChunk);
final Ticket<T> current = (Ticket<T>)((ChunkSystemSortedArraySet<Ticket<?>>)ticketsAtChunk).moonrise$replace(ticket);
final Ticket current = (Ticket)((ChunkSystemSortedArraySet<Ticket>)ticketsAtChunk).moonrise$replace(ticket);
final int levelAfter = getTicketLevelAt(ticketsAtChunk);
if (current != ticket) {
@@ -562,6 +599,7 @@ public final class ChunkHolderManager {
if (removeDelay != NO_TIMEOUT_MARKER) {
this.addExpireCount(chunkX, chunkZ);
}
this.addTicketCounter(type, chunk);
}
if (levelBefore != levelAfter) {
@@ -576,36 +614,85 @@ public final class ChunkHolderManager {
}
}
public <T> boolean removeTicketAtLevel(final TicketType<T> type, final ChunkPos chunkPos, final int level, final T identifier) {
private void addTicketCounter(final TicketType type, final long pos) {
final long[] counterTypes = ((ChunkSystemTicketType<?>)(Object)type).moonrise$getCounterTypes();
if (counterTypes.length == 0) {
return;
}
synchronized (this.ticketCounters) {
for (final long counterType : counterTypes) {
final Long2IntOpenHashMap oldCounters = this.ticketCounters.get(counterType);
final Long2IntOpenHashMap newCounters = oldCounters == null ? new Long2IntOpenHashMap() : oldCounters.clone();
newCounters.addTo(pos, 1);
this.ticketCounters.put(counterType, newCounters);
}
}
}
private void removeTicketCounter(final TicketType type, final long pos) {
final long[] counterTypes = ((ChunkSystemTicketType<?>)(Object)type).moonrise$getCounterTypes();
if (counterTypes.length == 0) {
return;
}
synchronized (this.ticketCounters) {
for (final long counterType : counterTypes) {
final Long2IntOpenHashMap oldCounters = this.ticketCounters.get(counterType);
final Long2IntOpenHashMap newCounters = oldCounters == null ? new Long2IntOpenHashMap() : oldCounters.clone();
final int currCount = newCounters.get(pos);
if (currCount <= 0) {
throw new IllegalStateException("Count must be > 0 at this stage");
}
if (currCount == 1) {
newCounters.remove(pos);
} else {
newCounters.put(pos, currCount - 1);
}
this.ticketCounters.put(counterType, newCounters);
}
}
}
public Long2IntOpenHashMap getTicketCounters(final long counterType) {
return this.ticketCounters.get(counterType);
}
public <T> boolean removeTicketAtLevel(final TicketType type, final ChunkPos chunkPos, final int level, final T identifier) {
return this.removeTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkPos), level, identifier);
}
public <T> boolean removeTicketAtLevel(final TicketType<T> type, final int chunkX, final int chunkZ, final int level, final T identifier) {
public <T> boolean removeTicketAtLevel(final TicketType type, final int chunkX, final int chunkZ, final int level, final T identifier) {
return this.removeTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkX, chunkZ), level, identifier);
}
public <T> boolean removeTicketAtLevel(final TicketType<T> type, final long chunk, final int level, final T identifier) {
public <T> boolean removeTicketAtLevel(final TicketType type, final long chunk, final int level, final T identifier) {
return this.removeTicketAtLevel(type, chunk, level, identifier, true);
}
<T> boolean removeTicketAtLevel(final TicketType<T> type, final long chunk, final int level, final T identifier, final boolean lock) {
<T> boolean removeTicketAtLevel(final TicketType type, final long chunk, final int level, final T identifier, final boolean lock) {
if (level > MAX_TICKET_LEVEL) {
return false;
}
final int chunkX = CoordinateUtils.getChunkX(chunk);
final int chunkZ = CoordinateUtils.getChunkZ(chunk);
final Ticket<T> probe = new Ticket<>(type, level, identifier);
final Ticket probe = new Ticket(type, level, 0L);
((ChunkSystemTicket<T>)(Object)probe).moonrise$setIdentifier(identifier);
final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null;
try {
final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.get(chunk);
final SortedArraySet<Ticket> ticketsAtChunk = this.tickets.get(chunk);
if (ticketsAtChunk == null) {
return false;
}
final int oldLevel = getTicketLevelAt(ticketsAtChunk);
final Ticket<T> ticket = (Ticket<T>)((ChunkSystemSortedArraySet<Ticket<?>>)ticketsAtChunk).moonrise$removeAndGet(probe);
final Ticket ticket = (Ticket)((ChunkSystemSortedArraySet<Ticket>)ticketsAtChunk).moonrise$removeAndGet(probe);
if (ticket == null) {
return false;
@@ -614,8 +701,7 @@ public final class ChunkHolderManager {
final int newLevel = getTicketLevelAt(ticketsAtChunk);
// we should not change the ticket levels while the target region may be ticking
if (oldLevel != newLevel) {
final Ticket<ChunkPos> unknownTicket = new Ticket<>(TicketType.UNKNOWN, level, new ChunkPos(chunk));
((ChunkSystemTicket<ChunkPos>)(Object)unknownTicket).moonrise$setRemoveDelay(Math.max(1, TicketType.UNKNOWN.timeout));
final Ticket unknownTicket = new Ticket(TicketType.UNKNOWN, level);
if (ticketsAtChunk.add(unknownTicket)) {
this.addExpireCount(chunkX, chunkZ);
} else {
@@ -628,6 +714,8 @@ public final class ChunkHolderManager {
this.removeExpireCount(chunkX, chunkZ);
}
this.removeTicketCounter(type, chunk);
return true;
} finally {
if (ticketLock != null) {
@@ -637,8 +725,8 @@ public final class ChunkHolderManager {
}
// atomic with respect to all add/remove/addandremove ticket calls for the given chunk
public <T, V> void addAndRemoveTickets(final long chunk, final TicketType<T> addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) {
public <T, V> void addAndRemoveTickets(final long chunk, final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType removeType, final int removeLevel, final V removeIdentifier) {
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(chunk), CoordinateUtils.getChunkZ(chunk));
try {
this.addTicketAtLevel(addType, chunk, addLevel, addIdentifier, false);
@@ -649,8 +737,8 @@ public final class ChunkHolderManager {
}
// atomic with respect to all add/remove/addandremove ticket calls for the given chunk
public <T, V> boolean addIfRemovedTicket(final long chunk, final TicketType<T> addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) {
public <T, V> boolean addIfRemovedTicket(final long chunk, final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType removeType, final int removeLevel, final V removeIdentifier) {
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(chunk), CoordinateUtils.getChunkZ(chunk));
try {
if (this.removeTicketAtLevel(removeType, chunk, removeLevel, removeIdentifier, false)) {
@@ -663,7 +751,7 @@ public final class ChunkHolderManager {
}
}
public <T> void removeAllTicketsFor(final TicketType<T> ticketType, final int ticketLevel, final T ticketIdentifier) {
public <T> void removeAllTicketsFor(final TicketType ticketType, final int ticketLevel, final T ticketIdentifier) {
if (ticketLevel > MAX_TICKET_LEVEL) {
return;
}
@@ -709,7 +797,7 @@ public final class ChunkHolderManager {
final int sectionShift = ((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift();
final Predicate<Ticket<?>> expireNow = (final Ticket<?> ticket) -> {
final Predicate<Ticket> expireNow = (final Ticket ticket) -> {
long removeDelay = ((ChunkSystemTicket<?>)(Object)ticket).moonrise$getRemoveDelay();
if (removeDelay == NO_TIMEOUT_MARKER) {
return false;
@@ -719,6 +807,9 @@ public final class ChunkHolderManager {
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();) {
final long sectionKey = iterator.nextLong();
@@ -727,9 +818,16 @@ public final class ChunkHolderManager {
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(
CoordinateUtils.getChunkX(sectionKey) << sectionShift,
CoordinateUtils.getChunkZ(sectionKey) << sectionShift
((lowerChunkX >> ticketShift) - 1) << ticketShift,
((lowerChunkZ >> ticketShift) - 1) << ticketShift,
(((lowerChunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((lowerChunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
);
try {
@@ -745,7 +843,7 @@ public final class ChunkHolderManager {
final long chunkKey = entry.getLongKey();
final int expireCount = entry.getIntValue();
final SortedArraySet<Ticket<?>> tickets = this.tickets.get(chunkKey);
final SortedArraySet<Ticket> tickets = this.tickets.get(chunkKey);
final int levelBefore = getTicketLevelAt(tickets);
final int sizeBefore = tickets.size();
@@ -776,9 +874,23 @@ public final class ChunkHolderManager {
if (chunkToExpireCount.isEmpty()) {
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 {
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();
@@ -816,7 +928,7 @@ public final class ChunkHolderManager {
private NewChunkHolder createChunkHolder(final long position) {
final NewChunkHolder ret = new NewChunkHolder(this.world, CoordinateUtils.getChunkX(position), CoordinateUtils.getChunkZ(position), this.taskScheduler);
ChunkSystem.onChunkHolderCreate(this.world, ret.vanillaChunkHolder);
PlatformHooks.get().onChunkHolderCreate(this.world, ret.vanillaChunkHolder);
return ret;
}
@@ -1005,14 +1117,9 @@ public final class ChunkHolderManager {
return;
}
if (!TickThread.isTickThread()) {
this.taskScheduler.scheduleChunkTask(() -> {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
pendingFullLoadUpdate.add(changedFullStatus.get(i));
}
ChunkHolderManager.this.processPendingFullUpdate();
}, Priority.HIGHEST);
// These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
// which will invoke processTicketUpdates
this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus);
} else {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
@@ -1024,7 +1131,7 @@ public final class ChunkHolderManager {
private void removeChunkHolder(final NewChunkHolder holder) {
holder.onUnload();
this.autoSaveQueue.remove(holder);
ChunkSystem.onChunkHolderDelete(this.world, holder.vanillaChunkHolder);
PlatformHooks.get().onChunkHolderDelete(this.world, holder.vanillaChunkHolder);
this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ));
}
@@ -1163,7 +1270,7 @@ public final class ChunkHolderManager {
this.removeChunkHolder(holder);
} else {
// add cooldown so the next unload check is not immediately next tick
this.addTicketAtLevel(UNLOAD_COOLDOWN, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ), MAX_TICKET_LEVEL, Unit.INSTANCE, false);
this.addTicketAtLevel(UNLOAD_COOLDOWN, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ), MAX_TICKET_LEVEL, null, false);
}
}
} finally {
@@ -1187,42 +1294,42 @@ public final class ChunkHolderManager {
public static record TicketOperation<T, V> (
TicketOperationType op, long chunkCoord,
TicketType<T> ticketType, int ticketLevel, T identifier,
TicketType<V> ticketType2, int ticketLevel2, V identifier2
TicketType ticketType, int ticketLevel, T identifier,
TicketType ticketType2, int ticketLevel2, V identifier2
) {
private TicketOperation(TicketOperationType op, long chunkCoord,
TicketType<T> ticketType, int ticketLevel, T identifier) {
TicketType ticketType, int ticketLevel, T identifier) {
this(op, chunkCoord, ticketType, ticketLevel, identifier, null, 0, null);
}
public static <T> TicketOperation<T, T> addOp(final ChunkPos chunk, final TicketType<T> type, final int ticketLevel, final T identifier) {
public static <T> TicketOperation<T, T> addOp(final ChunkPos chunk, final TicketType type, final int ticketLevel, final T identifier) {
return addOp(CoordinateUtils.getChunkKey(chunk), type, ticketLevel, identifier);
}
public static <T> TicketOperation<T, T> addOp(final int chunkX, final int chunkZ, final TicketType<T> type, final int ticketLevel, final T identifier) {
public static <T> TicketOperation<T, T> addOp(final int chunkX, final int chunkZ, final TicketType type, final int ticketLevel, final T identifier) {
return addOp(CoordinateUtils.getChunkKey(chunkX, chunkZ), type, ticketLevel, identifier);
}
public static <T> TicketOperation<T, T> addOp(final long chunk, final TicketType<T> type, final int ticketLevel, final T identifier) {
public static <T> TicketOperation<T, T> addOp(final long chunk, final TicketType type, final int ticketLevel, final T identifier) {
return new TicketOperation<>(TicketOperationType.ADD, chunk, type, ticketLevel, identifier);
}
public static <T> TicketOperation<T, T> removeOp(final ChunkPos chunk, final TicketType<T> type, final int ticketLevel, final T identifier) {
public static <T> TicketOperation<T, T> removeOp(final ChunkPos chunk, final TicketType type, final int ticketLevel, final T identifier) {
return removeOp(CoordinateUtils.getChunkKey(chunk), type, ticketLevel, identifier);
}
public static <T> TicketOperation<T, T> removeOp(final int chunkX, final int chunkZ, final TicketType<T> type, final int ticketLevel, final T identifier) {
public static <T> TicketOperation<T, T> removeOp(final int chunkX, final int chunkZ, final TicketType type, final int ticketLevel, final T identifier) {
return removeOp(CoordinateUtils.getChunkKey(chunkX, chunkZ), type, ticketLevel, identifier);
}
public static <T> TicketOperation<T, T> removeOp(final long chunk, final TicketType<T> type, final int ticketLevel, final T identifier) {
public static <T> TicketOperation<T, T> removeOp(final long chunk, final TicketType type, final int ticketLevel, final T identifier) {
return new TicketOperation<>(TicketOperationType.REMOVE, chunk, type, ticketLevel, identifier);
}
public static <T, V> TicketOperation<T, V> addIfRemovedOp(final long chunk,
final TicketType<T> addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) {
final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType removeType, final int removeLevel, final V removeIdentifier) {
return new TicketOperation<>(
TicketOperationType.ADD_IF_REMOVED, chunk, addType, addLevel, addIdentifier,
removeType, removeLevel, removeIdentifier
@@ -1230,8 +1337,8 @@ public final class ChunkHolderManager {
}
public static <T, V> TicketOperation<T, V> addAndRemove(final long chunk,
final TicketType<T> addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) {
final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType removeType, final int removeLevel, final V removeIdentifier) {
return new TicketOperation<>(
TicketOperationType.ADD_AND_REMOVE, chunk, addType, addLevel, addIdentifier,
removeType, removeLevel, removeIdentifier
@@ -1293,36 +1400,20 @@ public final class ChunkHolderManager {
}
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) {
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");
}
List<NewChunkHolder> changedFullStatus = null;
final boolean isTickThread = TickThread.isTickThread();
boolean ret = false;
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
final boolean canProcessScheduling = scheduledTasks == null;
if (this.ticketLevelPropagator.hasPendingUpdates()) {
if (scheduledTasks == null) {
scheduledTasks = new ArrayList<>();
}
changedFullStatus = new ArrayList<>();
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
this.blockTicketUpdates();
try {
@@ -1333,27 +1424,42 @@ public final class ChunkHolderManager {
} finally {
this.unblockTicketUpdates(Boolean.FALSE);
}
}
if (changedFullStatus != null) {
this.addChangedStatuses(changedFullStatus);
}
if (canProcessScheduling && scheduledTasks != null) {
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
scheduledTasks.get(i).schedule();
}
}
if (canProcessFullUpdates) {
if (isTickThread) {
ret |= this.processPendingFullUpdate();
}
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
private boolean processPendingFullUpdate() {
this.processOffThreadFullUpdates();
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
boolean ret = false;
@@ -1390,11 +1496,11 @@ public final class ChunkHolderManager {
final JsonArray allTicketsJson = new JsonArray();
ret.add("tickets", allTicketsJson);
for (final Iterator<ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket<?>>>> iterator = this.tickets.entryIterator();
for (final Iterator<ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>>> iterator = this.tickets.entryIterator();
iterator.hasNext();) {
final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket<?>>> coordinateTickets = iterator.next();
final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>> coordinateTickets = iterator.next();
final long coordinate = coordinateTickets.getKey();
final SortedArraySet<Ticket<?>> tickets = coordinateTickets.getValue();
final SortedArraySet<Ticket> tickets = coordinateTickets.getValue();
final JsonObject coordinateJson = new JsonObject();
allTicketsJson.add(coordinateJson);
@@ -1409,17 +1515,17 @@ public final class ChunkHolderManager {
// directly over the set using the iterator
// however, it also means we need to null-check the values, and there is a possibility that we _miss_ an
// entry OR iterate over an entry multiple times
for (final Object ticketUncasted : ((ChunkSystemSortedArraySet<Ticket<?>>)tickets).moonrise$copyBackingArray()) {
for (final Object ticketUncasted : ((ChunkSystemSortedArraySet<Ticket>)tickets).moonrise$copyBackingArray()) {
if (ticketUncasted == null) {
continue;
}
final Ticket<?> ticket = (Ticket<?>)ticketUncasted;
final Ticket ticket = (Ticket)ticketUncasted;
final JsonObject ticketSerialized = new JsonObject();
ticketsSerialized.add(ticketSerialized);
ticketSerialized.addProperty("type", ticket.getType().toString());
ticketSerialized.addProperty("level", Integer.valueOf(ticket.getTicketLevel()));
ticketSerialized.addProperty("identifier", Objects.toString(ticket.key));
ticketSerialized.addProperty("identifier", Objects.toString(((ChunkSystemTicket<?>)(Object)ticket).moonrise$getIdentifier()));
ticketSerialized.addProperty("remove_tick", Long.valueOf(((ChunkSystemTicket<?>)(Object)ticket).moonrise$getRemoveDelay()));
}
}

View File

@@ -22,6 +22,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkProgres
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkUpgradeGenericStatusTask;
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer;
import ca.spottedleaf.moonrise.patches.chunk_system.status.ChunkSystemChunkStep;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@@ -73,35 +74,35 @@ public final class ChunkTaskScheduler {
LOGGER.info("Chunk system is using population gen parallelism: " + useParallelGen);
}
public static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_system:chunk_load", Long::compareTo);
public static final TicketType CHUNK_LOAD = ChunkSystemTicketType.create("chunk_system:chunk_load", Long::compareTo);
private static final AtomicLong CHUNK_LOAD_IDS = new AtomicLong();
public static Long getNextChunkLoadId() {
return Long.valueOf(CHUNK_LOAD_IDS.getAndIncrement());
}
public static final TicketType<Long> NON_FULL_CHUNK_LOAD = TicketType.create("chunk_system:non_full_load", Long::compareTo);
public static final TicketType NON_FULL_CHUNK_LOAD = ChunkSystemTicketType.create("chunk_system:non_full_load", Long::compareTo);
private static final AtomicLong NON_FULL_CHUNK_LOAD_IDS = new AtomicLong();
public static Long getNextNonFullLoadId() {
return Long.valueOf(NON_FULL_CHUNK_LOAD_IDS.getAndIncrement());
}
public static final TicketType<Long> ENTITY_LOAD = TicketType.create("chunk_system:entity_load", Long::compareTo);
public static final TicketType ENTITY_LOAD = ChunkSystemTicketType.create("chunk_system:entity_load", Long::compareTo);
private static final AtomicLong ENTITY_LOAD_IDS = new AtomicLong();
public static Long getNextEntityLoadId() {
return Long.valueOf(ENTITY_LOAD_IDS.getAndIncrement());
}
public static final TicketType<Long> POI_LOAD = TicketType.create("chunk_system:poi_load", Long::compareTo);
public static final TicketType POI_LOAD = ChunkSystemTicketType.create("chunk_system:poi_load", Long::compareTo);
private static final AtomicLong POI_LOAD_IDS = new AtomicLong();
public static Long getNextPoiLoadId() {
return Long.valueOf(POI_LOAD_IDS.getAndIncrement());
}
public static final TicketType<Long> CHUNK_RELIGHT = TicketType.create("starlight:chunk_relight", Long::compareTo);
public static final TicketType CHUNK_RELIGHT = ChunkSystemTicketType.create("starlight:chunk_relight", Long::compareTo);
private static final AtomicLong CHUNK_RELIGHT_IDS = new AtomicLong();
public static Long getNextChunkRelightId() {
@@ -271,6 +272,16 @@ public final class ChunkTaskScheduler {
return this.lockShift;
}
private volatile boolean shutdown;
public boolean hasShutdown() {
return this.shutdown;
}
public void setShutdown(final boolean shutdown) {
this.shutdown = shutdown;
}
public ChunkTaskScheduler(final ServerLevel world) {
this.world = world;
// must be >= region shift (in paper, doesn't exist) and must be >= ticket propagator section shift
@@ -525,6 +536,13 @@ public final class ChunkTaskScheduler {
return loaded;
}
if (this.hasShutdown()) {
throw new IllegalStateException(
"Chunk system has shut down, cannot process chunk requests in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.world) + "' at "
+ "(" + chunkX + "," + chunkZ + ") status: " + status
);
}
final Long ticketId = getNextNonFullLoadId();
final int ticketLevel = getTicketLevel(status);
this.chunkHolderManager.addTicketAtLevel(NON_FULL_CHUNK_LOAD, chunkX, chunkZ, ticketLevel, ticketId);

View File

@@ -11,7 +11,6 @@ import ca.spottedleaf.moonrise.common.misc.LazyRunnable;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
@@ -118,9 +117,11 @@ public final class NewChunkHolder {
if (!transientChunk) {
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);
}
}
@@ -1270,10 +1271,10 @@ public final class NewChunkHolder {
// state upgrade
if (!current.isOrAfter(FullChunkStatus.FULL) && pending.isOrAfter(FullChunkStatus.FULL)) {
this.updateCurrentState(FullChunkStatus.FULL);
ChunkSystem.onChunkPreBorder(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkPreBorder(chunk, this.vanillaChunkHolder);
this.scheduler.chunkHolderManager.ensureInAutosave(this);
this.changeEntityChunkStatus(FullChunkStatus.FULL);
ChunkSystem.onChunkBorder(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkBorder(chunk, this.vanillaChunkHolder);
this.onFullChunkLoadChange(true, changedFullStatus);
this.completeFullStatusConsumers(FullChunkStatus.FULL, chunk);
}
@@ -1281,34 +1282,34 @@ public final class NewChunkHolder {
if (!current.isOrAfter(FullChunkStatus.BLOCK_TICKING) && pending.isOrAfter(FullChunkStatus.BLOCK_TICKING)) {
this.updateCurrentState(FullChunkStatus.BLOCK_TICKING);
this.changeEntityChunkStatus(FullChunkStatus.BLOCK_TICKING);
ChunkSystem.onChunkTicking(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkTicking(chunk, this.vanillaChunkHolder);
this.completeFullStatusConsumers(FullChunkStatus.BLOCK_TICKING, chunk);
}
if (!current.isOrAfter(FullChunkStatus.ENTITY_TICKING) && pending.isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
this.updateCurrentState(FullChunkStatus.ENTITY_TICKING);
this.changeEntityChunkStatus(FullChunkStatus.ENTITY_TICKING);
ChunkSystem.onChunkEntityTicking(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkEntityTicking(chunk, this.vanillaChunkHolder);
this.completeFullStatusConsumers(FullChunkStatus.ENTITY_TICKING, chunk);
}
} else {
if (current.isOrAfter(FullChunkStatus.ENTITY_TICKING) && !pending.isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
this.changeEntityChunkStatus(FullChunkStatus.BLOCK_TICKING);
ChunkSystem.onChunkNotEntityTicking(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkNotEntityTicking(chunk, this.vanillaChunkHolder);
this.updateCurrentState(FullChunkStatus.BLOCK_TICKING);
}
if (current.isOrAfter(FullChunkStatus.BLOCK_TICKING) && !pending.isOrAfter(FullChunkStatus.BLOCK_TICKING)) {
this.changeEntityChunkStatus(FullChunkStatus.FULL);
ChunkSystem.onChunkNotTicking(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkNotTicking(chunk, this.vanillaChunkHolder);
this.updateCurrentState(FullChunkStatus.FULL);
}
if (current.isOrAfter(FullChunkStatus.FULL) && !pending.isOrAfter(FullChunkStatus.FULL)) {
this.onFullChunkLoadChange(false, changedFullStatus);
this.changeEntityChunkStatus(FullChunkStatus.INACCESSIBLE);
ChunkSystem.onChunkNotBorder(chunk, this.vanillaChunkHolder);
ChunkSystem.onChunkPostNotBorder(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkNotBorder(chunk, this.vanillaChunkHolder);
PlatformHooks.get().onChunkPostNotBorder(chunk, this.vanillaChunkHolder);
this.updateCurrentState(FullChunkStatus.INACCESSIBLE);
}
}

View File

@@ -70,7 +70,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
}
((ChunkSystemLevelChunk)chunk).moonrise$setChunkAndHolder(new ServerChunkCache.ChunkAndHolder(chunk, this.chunkHolder.vanillaChunkHolder));
((ChunkSystemLevelChunk)chunk).moonrise$setChunkHolder(this.chunkHolder);
final NewChunkHolder chunkHolder = this.chunkHolder;

View File

@@ -6,4 +6,8 @@ public interface ChunkSystemTicket<T> {
public void moonrise$setRemoveDelay(final long removeDelay);
public T moonrise$getIdentifier();
public void moonrise$setIdentifier(final T identifier);
}

View File

@@ -0,0 +1,11 @@
package ca.spottedleaf.moonrise.patches.chunk_system.ticket;
import net.minecraft.server.level.ChunkMap;
public interface ChunkSystemTicketStorage {
public ChunkMap moonrise$getChunkMap();
public void moonrise$setChunkMap(final ChunkMap chunkMap);
}

View File

@@ -0,0 +1,34 @@
package ca.spottedleaf.moonrise.patches.chunk_system.ticket;
import net.minecraft.server.level.TicketType;
import java.util.Comparator;
public interface ChunkSystemTicketType<T> {
public static final long COUNTER_TYPE_FORCED = 0L;
// used only by neoforge
public static final long COUNTER_TYPER_NATURAL_SPAWNING_FORCED = 1L;
public static <T> TicketType create(final String name, final Comparator<T> identifierComparator) {
return create(name, identifierComparator, 0L);
}
public static <T> TicketType create(final String name, final Comparator<T> identifierComparator, final long timeout) {
// note: cannot persist unless registered
final TicketType ret = new TicketType(timeout, false, TicketType.TicketUse.LOADING_AND_SIMULATION);
((ChunkSystemTicketType<T>)(Object)ret).moonrise$setIdentifierComparator(identifierComparator);
return ret;
}
public long moonrise$getId();
public Comparator<T> moonrise$getIdentifierComparator();
public void moonrise$setIdentifierComparator(final Comparator<T> comparator);
public long[] moonrise$getCounterTypes();
public void moonrise$setTimeout(final long to);
}

View File

@@ -3,5 +3,13 @@ package ca.spottedleaf.moonrise.patches.chunk_tick_iteration;
public final class ChunkTickConstants {
public static final int PLAYER_SPAWN_TRACK_RANGE = 8;
// the smallest distance on x/z is at 45 degrees, we need to subtract 0.5 since this is calculated from chunk center and not chunk perimeter
// note: vanilla does not subtract 0.5 but the result is (luckily!) the same
public static final int NARROW_SPAWN_TRACK_RANGE = (int)Math.floor(((double)PLAYER_SPAWN_TRACK_RANGE / Math.sqrt(2.0)) - 0.5);
static {
if (NARROW_SPAWN_TRACK_RANGE < 0) {
throw new IllegalStateException();
}
}
}

View File

@@ -13,4 +13,6 @@ public interface ChunkTickDistanceManager {
final SectionPos oldPos, final SectionPos newPos,
final boolean oldIgnore, final boolean newIgnore);
public boolean moonrise$hasAnyNearbyNarrow(final int chunkX, final int chunkZ);
}

View File

@@ -6,7 +6,7 @@ import net.minecraft.world.level.chunk.LevelChunk;
public interface ChunkTickServerLevel {
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks();
public ReferenceList<LevelChunk> moonrise$getPlayerTickingChunks();
public void moonrise$markChunkForPlayerTicking(final LevelChunk chunk);

Some files were not shown because too many files have changed in this diff Show More