Compare commits

...

49 Commits

Author SHA1 Message Date
Spottedleaf
53e3fe7f32 Set version to 0.3.0-SNAPSHOT 2025-06-09 02:27:49 -07:00
Spottedleaf
292d3ca982 Set version to 0.3.0-beta.2 2025-06-09 02:25:32 -07:00
Spottedleaf
25ce72434f 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:23:45 -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
98 changed files with 2244 additions and 1388 deletions

View File

@@ -31,8 +31,6 @@ jobs:
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }} key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: | restore-keys: |
${{ runner.os }}-project-local-gradle-caches- ${{ runner.os }}-project-local-gradle-caches-
- name: "setup dependencies"
run: ./install_deps.sh
- name: "execute gradle build" - name: "execute gradle build"
run: ./gradlew build run: ./gradlew build
- name: Determine Snapshot Status - 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) # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar !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 import me.modmuss50.mpp.ReleaseType
plugins { plugins {
id("xyz.jpenilla.quiet-architectury-loom") id("java-library")
id("me.modmuss50.mod-publish-plugin") version "0.7.2" apply false id("net.neoforged.moddev")
id("me.modmuss50.mod-publish-plugin") version "0.8.4" apply false
} }
/* extensions.create("runConfigCommon", RunConfigCommon.class)
* Gets the version name from the latest Git tag
*/ def getGitCommit = providers.exec {
// https://stackoverflow.com/questions/28498688/gradle-script-to-autoversion-and-include-the-commit-hash-in-android commandLine 'git', 'rev-parse', '--short', 'HEAD'
def getGitCommit = { -> }.standardOutput.getAsText().map { it.trim() }
def stdout = new ByteArrayOutputStream()
exec { def aw2at = Aw2AtTask.configureDefault(
commandLine 'git', 'rev-parse', '--short', 'HEAD' getProject(),
standardOutput = stdout layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
} sourceSets.main
return stdout.toString().trim() )
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 { 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:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") 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 { allprojects {
group = rootProject.maven_group 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 { java {
withSourcesJar() 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 { repositories {
mavenLocal { maven {
url = "https://repo.papermc.io/repository/maven-public/"
mavenContent { mavenContent {
includeModule("ca.spottedleaf", "concurrentutil") includeGroup("ca.spottedleaf")
includeModule("ca.spottedleaf", "yamlconfig")
} }
} }
maven { maven {
url "https://api.modrinth.com/maven" url = "https://api.modrinth.com/maven"
mavenContent { mavenContent {
includeGroup("maven.modrinth") includeGroup("maven.modrinth")
} }
} }
maven { url "https://maven.shedaniel.me/" } maven { url = "https://maven.shedaniel.me/" }
maven { url "https://maven.terraformersmc.com/releases/" } maven { url = "https://maven.terraformersmc.com/releases/" }
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
} }
// make build reproducible // make build reproducible
@@ -82,32 +98,22 @@ allprojects {
rename { "${it}_${rootProject.base.archivesName.get()}"} rename { "${it}_${rootProject.base.archivesName.get()}"}
} }
} }
loom {
accessWidenerPath = awFile
mixin {
useLegacyMixinAp = false
}
}
} }
subprojects { 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("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 { publishMods {
file = remapJar.archiveFile
if (project.version.contains("-beta.")) { if (project.version.contains("-beta.")) {
type = ReleaseType.BETA type = ReleaseType.BETA
} else { } else {
@@ -131,20 +137,7 @@ subprojects {
} }
// Setup a run with lithium for compatibility testing // Setup a run with lithium for compatibility testing
sourceSets.create("lithium")
configurations.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 { dependencies {
String coordinates = "maven.modrinth:lithium:" String coordinates = "maven.modrinth:lithium:"
if (getProject().name == "Moonrise-NeoForge") { if (getProject().name == "Moonrise-NeoForge") {
@@ -152,10 +145,6 @@ subprojects {
} else { } else {
coordinates += rootProject.fabric_lithium_version 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,50 @@
plugins { import java.util.stream.Collectors
id("xyz.jpenilla.quiet-architectury-loom") import net.fabricmc.loom.util.gradle.SourceSetHelper
id 'maven-publish'
id 'com.gradleup.shadow'
}
configurations.create("libs") plugins {
configurations.shadow { id("quiet-fabric-loom")
extendsFrom(configurations.libs) id 'maven-publish'
}
configurations.implementation {
extendsFrom(configurations.libs)
} }
dependencies { dependencies {
add('shadow', project([path: ":", configuration: "namedElements"])) minecraft "com.mojang:minecraft:${project.minecraft_version}"
runtimeOnly(project(":").sourceSets.main.output) mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") runtimeOnly(project(":").sourceSets.main.output)
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") 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}") libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}" modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
include "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}" modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
modImplementation platform(fabricApiLibs.bom)
modImplementation fabricApiLibs.command.api.v2 modImplementation fabricApiLibs.command.api.v2
modImplementation fabricApiLibs.lifecycle.events.v1 modImplementation fabricApiLibs.lifecycle.events.v1
include fabricApiLibs.command.api.v2 include fabricApiLibs.command.api.v2
include fabricApiLibs.base include fabricApiLibs.base
} }
processResources { tasks.processResources {
inputs.property "version", project.version def properties = [
"version": project.version,
"minecraft_version": minecraft_version,
"loader_version": loader_version,
"mod_version": mod_version
]
inputs.properties(properties)
filesMatching("fabric.mod.json") { filesMatching("fabric.mod.json") {
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version expand properties
} }
} }
shadowJar { tasks.shadowJar {
archiveClassifier = "dev-all" archiveClassifier = "dev-all"
destinationDirectory = layout.buildDirectory.dir("libs") destinationDirectory = layout.buildDirectory.dir("libs")
configurations = [project.configurations.shadow] configurations = [project.configurations.shadow]
@@ -49,6 +54,7 @@ shadowJar {
} }
publishMods { publishMods {
file = remapJar.archiveFile
modLoaders = ["fabric"] modLoaders = ["fabric"]
modrinth { modrinth {
@@ -66,3 +72,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,13 @@
package ca.spottedleaf.moonrise.fabric; package ca.spottedleaf.moonrise.fabric;
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
import ca.spottedleaf.moonrise.common.PlatformHooks; import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder; 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.DSL;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic; import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrays;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@@ -14,7 +17,9 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder; import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -27,10 +32,11 @@ import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData; import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
public final class 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 boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
@@ -111,13 +117,43 @@ public final class FabricHooks implements PlatformHooks {
@Override @Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate, public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) { 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 @Override
public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest, final AABB boundingBox, 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) { 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 @Override
@@ -222,4 +258,9 @@ public final class FabricHooks implements PlatformHooks {
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) { public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange; return currentRange;
} }
@Override
public long[] getCounterTypesUncached(final TicketType type) {
return type == TicketType.FORCED ? new long[] { ChunkSystemTicketType.COUNTER_TYPE_FORCED } : LongArrays.EMPTY_ARRAY;
}
} }

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", "accessWidener": "moonrise.accesswidener",
"depends": { "depends": {
"fabricloader": ">=${loader_version}", "fabricloader": ">=${loader_version}",
"minecraft": ">1.21.1 <1.21.4", "minecraft": ">1.21.4 <1.21.6",
"fabric-command-api-v2": "*" "fabric-command-api-v2": "*"
}, },
"custom": { "custom": {

View File

@@ -2,7 +2,6 @@
"parent": "moonrise.mixins.json", "parent": "moonrise.mixins.json",
"package": "ca.spottedleaf.moonrise.fabric.mixin", "package": "ca.spottedleaf.moonrise.fabric.mixin",
"mixins": [ "mixins": [
"chunk_system.FabricDistanceManagerMixin",
"chunk_system.FabricMinecraftServerMixin", "chunk_system.FabricMinecraftServerMixin",
"chunk_system.FabricServerLevelMixin", "chunk_system.FabricServerLevelMixin",
"collisions.EntityMixin" "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,26 @@
# Done to increase the memory available to gradle. # Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
# Fabric Properties # Fabric Properties
# check these on https://modmuss50.me/fabric.html # check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.3 minecraft_version=1.21.5
loader_version=0.16.7 loader_version=0.16.14
supported_minecraft_versions=1.21.3 supported_minecraft_versions=1.21.5
neoforge_version=21.3.31-beta neoforge_version=21.5.65-beta
fabric_api_version=0.107.0+1.21.3 neoform_version=1.21.5-20250325.162830
fabric_api_version=0.123.0+1.21.5
snakeyaml_version=2.3 snakeyaml_version=2.3
concurrentutil_version=0.0.2-SNAPSHOT concurrentutil_version=0.0.3
yamlconfig_version=1.0.2-SNAPSHOT yamlconfig_version=1.0.2
cloth_version=16.0.141 cloth_version=18.0.145
modmenu_version=12.0.0-beta.1 modmenu_version=14.0.0-rc.2
junit_version=5.11.3
# version ids from modrinth # version ids from modrinth
fabric_lithium_version=QhCwdt4l fabric_lithium_version=nhc57Td2
neo_lithium_version=wDD955sb neo_lithium_version=P5VT33Jo
# Mod Properties # Mod Properties
mod_version=0.2.0-beta.5 mod_version=0.3.0-SNAPSHOT
maven_group=ca.spottedleaf.moonrise maven_group=ca.spottedleaf.moonrise
archives_base_name=moonrise archives_base_name=moonrise

Binary file not shown.

View File

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

3
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # 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 APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

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 { plugins {
id("xyz.jpenilla.quiet-architectury-loom") id("net.neoforged.moddev")
id 'maven-publish' id 'maven-publish'
id 'com.gradleup.shadow'
} }
repositories { repositories {
@@ -13,32 +14,66 @@ repositories {
} }
} }
configurations.implementation { def aw2at = Aw2AtTask.configureDefault(
extendsFrom(configurations.shadow) getProject(),
} rootProject.layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
sourceSets.main
)
dependencies { neoForge {
add('shadow', project([path: ":", configuration: "namedElements"])) version = rootProject.neoforge_version
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" validateAccessTransformers = true
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") mods {
shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") moonrise {
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") sourceSet sourceSets.main
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}") sourceSet rootProject.sourceSets.main
}
modImplementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" }
include "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}" runs {
} client {
client()
processResources { }
inputs.property "version", project.version server {
server()
filesMatching("META-INF/neoforge.mods.toml") { }
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version }
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" archiveClassifier = "dev-all"
destinationDirectory = layout.buildDirectory.dir("libs") destinationDirectory = layout.buildDirectory.dir("libs")
configurations = [project.configurations.shadow] configurations = [project.configurations.shadow]
@@ -47,9 +82,20 @@ shadowJar {
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml' 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 { publishMods {
file = productionJar.archiveFile
modLoaders = ["neoforge"] modLoaders = ["neoforge"]
modrinth { 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,14 @@
package ca.spottedleaf.moonrise.neoforge; package ca.spottedleaf.moonrise.neoforge;
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
import ca.spottedleaf.moonrise.common.PlatformHooks; import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder; import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils; 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.DSL;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic; import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtOps;
@@ -13,6 +16,7 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder; import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@@ -34,10 +38,11 @@ import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent;
import net.neoforged.neoforge.event.level.ChunkDataEvent; import net.neoforged.neoforge.event.level.ChunkDataEvent;
import net.neoforged.neoforge.event.level.ChunkEvent; import net.neoforged.neoforge.event.level.ChunkEvent;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
public final class NeoForgeHooks implements PlatformHooks { public final class NeoForgeHooks extends BaseChunkSystemHooks implements PlatformHooks {
@Override @Override
public String getBrand() { public String getBrand() {
@@ -114,7 +119,12 @@ public final class NeoForgeHooks implements PlatformHooks {
@Override @Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate, public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) { 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))) { if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
into.add(part); into.add(part);
} }
@@ -129,9 +139,18 @@ public final class NeoForgeHooks implements PlatformHooks {
return; 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); 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); into.add(casted);
if (into.size() >= maxCount) { if (into.size() >= maxCount) {
break; break;
@@ -245,4 +264,18 @@ public final class NeoForgeHooks implements PlatformHooks {
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) { public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return 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();
}
} }

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]] [[dependencies.moonrise]]
modId = "minecraft" modId = "minecraft"
type = "required" type = "required"
versionRange = "(1.21.1,1.21.4)" versionRange = "(1.21.4,1.21.6)"
ordering = "NONE" ordering = "NONE"
side = "BOTH" side = "BOTH"

View File

@@ -2,9 +2,9 @@
"parent": "moonrise.mixins.json", "parent": "moonrise.mixins.json",
"package": "ca.spottedleaf.moonrise.neoforge.mixin", "package": "ca.spottedleaf.moonrise.neoforge.mixin",
"mixins": [ "mixins": [
"chunk_system.NeoForgeDistanceManagerMixin",
"chunk_system.NeoForgeMinecraftServerMixin", "chunk_system.NeoForgeMinecraftServerMixin",
"chunk_system.NeoForgeServerLevelMixin", "chunk_system.NeoForgeServerLevelMixin",
"chunk_system.NeoForgeTicketStorageMixin",
"collisions.EntityMixin" "collisions.EntityMixin"
], ],
"client": [ "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 { maven {
name = 'jmp' name = 'jmp'
url = 'https://repo.jpenilla.xyz/snapshots' url = 'https://repo.jpenilla.xyz/snapshots'
mavenContent { snapshotsOnly() }
} }
maven { maven {
name = 'architectury' name = 'architectury'
@@ -23,9 +22,10 @@ pluginManagement {
} }
plugins { plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.300" apply false id("quiet-fabric-loom") version "1.10.317" apply false
id 'com.gradleup.shadow' version '8.3.0' apply false id("net.neoforged.moddev") version "2.0.80" apply false
id 'com.gradleup.shadow' version '8.3.6' apply false
} }
dependencyResolutionManagement { dependencyResolutionManagement {
@@ -48,3 +48,6 @@ include("fabric")
findProject(":fabric").name = "Moonrise-Fabric" findProject(":fabric").name = "Moonrise-Fabric"
include("neoforge") include("neoforge")
findProject(":neoforge").name = "Moonrise-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; package ca.spottedleaf.moonrise.common;
import ca.spottedleaf.moonrise.common.util.ChunkSystemHooks;
import com.mojang.datafixers.DSL; import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import net.minecraft.core.BlockPos; 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.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@@ -24,7 +24,7 @@ import java.util.List;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.function.Predicate; import java.util.function.Predicate;
public interface PlatformHooks { public interface PlatformHooks extends ChunkSystemHooks {
public static PlatformHooks get() { public static PlatformHooks get() {
return Holder.INSTANCE; return Holder.INSTANCE;
} }
@@ -64,8 +64,6 @@ public interface PlatformHooks {
public void entityMove(final Entity entity, final long oldSection, final long newSection); 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 configFixMC224294();
public boolean configAutoConfigSendDistance(); public boolean configAutoConfigSendDistance();

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.Reference2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.lang.reflect.Array;
import java.util.Arrays; import java.util.Arrays;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@@ -21,15 +22,34 @@ public final class IteratorSafeOrderedReferenceSet<E> {
private int iteratorCount; private int iteratorCount;
public IteratorSafeOrderedReferenceSet() { 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, public IteratorSafeOrderedReferenceSet(final int setCapacity, final float setLoadFactor, final int arrayCapacity,
final double maxFragFactor) { 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 = new Reference2IntLinkedOpenHashMap<>(setCapacity, setLoadFactor);
this.indexMap.defaultReturnValue(-1); this.indexMap.defaultReturnValue(-1);
this.maxFragFactor = maxFragFactor; 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() { public int createRawIterator() {
++this.iteratorCount; ++this.iteratorCount;
if (this.indexMap.isEmpty()) { if (this.indexMap.isEmpty()) {
return -1; return Integer.MAX_VALUE;
} else { } else {
return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0; 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() { public void finishRawIterator() {
@@ -205,10 +225,6 @@ public final class IteratorSafeOrderedReferenceSet<E> {
//this.check(); //this.check();
} }
public E rawGet(final int index) {
return this.listElements[index];
}
public int size() { public int size() {
// always returns the correct amount - listSize can be different // always returns the correct amount - listSize can be different
return this.indexMap.size(); return this.indexMap.size();

View File

@@ -1,9 +1,9 @@
package ca.spottedleaf.moonrise.common.misc; package ca.spottedleaf.moonrise.common.misc;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils; import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.MoonriseConstants; 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.ChunkSystemLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData; import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants; 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.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_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.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.TICK_VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getTickViewDistance(player));
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, ChunkSystem.getViewDistance(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 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; 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.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
@@ -23,12 +23,16 @@ public final class PositionCountingAreaMap<T> {
return this.positions.size(); return this.positions.size();
} }
public boolean hasObjectsNear(final long pos) {
return this.positions.containsKey(pos);
}
public boolean hasObjectsNear(final int toX, final int toZ) { 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) { 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) { 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 @Override
protected void addCallback(final T parameter, final int toX, final int toZ) { 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 @Override
protected void removeCallback(final T parameter, final int toX, final int toZ) { 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) { if (PositionCountingAreaMap.this.positions.addTo(key, -1) == 1) {
PositionCountingAreaMap.this.positions.remove(key); PositionCountingAreaMap.this.positions.remove(key);
} }

View File

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

@@ -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; package ca.spottedleaf.moonrise.common.util;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.Strictness;
import com.google.gson.internal.Streams; import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import java.io.File; import java.io.File;
@@ -16,7 +17,7 @@ public final class JsonUtil {
final StringWriter stringWriter = new StringWriter(); final StringWriter stringWriter = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(stringWriter); final JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(" "); jsonWriter.setIndent(" ");
jsonWriter.setLenient(false); jsonWriter.setStrictness(Strictness.LENIENT);
Streams.write(element, jsonWriter); Streams.write(element, jsonWriter);
final String jsonString = stringWriter.toString(); 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 final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
private static String getThreadContext() {
return "thread=" + Thread.currentThread().getName();
}
/** /**
* @deprecated * @deprecated
*/ */
@Deprecated @Deprecated
public static void ensureTickThread(final String reason) { public static void ensureTickThread(final String reason) {
if (!isTickThread()) { 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); throw new IllegalStateException(reason);
} }
} }
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) { public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) { if (!isTickThreadFor(world, pos)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); final String ex = "Thread failed main thread check: " +
throw new IllegalStateException(reason); 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) { public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) { if (!isTickThreadFor(world, pos)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); final String ex = "Thread failed main thread check: " +
throw new IllegalStateException(reason); 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) { public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) { if (!isTickThreadFor(world, chunkX, chunkZ)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); final String ex = "Thread failed main thread check: " +
throw new IllegalStateException(reason); 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) { public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) { if (!isTickThreadFor(entity)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); final String ex = "Thread failed main thread check: " +
throw new IllegalStateException(reason); 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) { public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) { if (!isTickThreadFor(world, aabb)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); final String ex = "Thread failed main thread check: " +
throw new IllegalStateException(reason); 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) { public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) { if (!isTickThreadFor(world, blockX, blockZ)) {
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); final String ex = "Thread failed main thread check: " +
throw new IllegalStateException(reason); 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(); 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) { public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
return isTickThread(); return isTickThread();
} }

View File

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

View File

@@ -125,7 +125,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @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); return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
} }
@@ -166,7 +166,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
*/ */
@Overwrite @Overwrite
public Map<Property<?>, Comparable<?>> getValues() { 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 // We have to use this.values until the table is loaded
return table.isLoaded() ? table.getMapView(this.tableIndex) : this.values; 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 * @reason Use ticket system to control ticket levels
* @author Spottedleaf * @author Spottedleaf
* @see net.minecraft.server.level.ServerChunkCache#addRegionTicket(TicketType, ChunkPos, int, Object) * @see net.minecraft.server.level.ServerChunkCache#addTicketWithRadius(TicketType, ChunkPos, int)
*/ */
@Overwrite @Overwrite
public void setTicketLevel(int i) { 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)} * @reason Chunk system hooks for ticket level updating now in {@link NewChunkHolder#processTicketLevelUpdate(List, List)}
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite // inject to avoid conflicting with fabric API's mixin here, we call their event in FabricHooks
public void updateFutures(final ChunkMap chunkMap, final Executor executor) { @Inject(
method = "updateFutures",
at = @At(
value = "HEAD"
)
)
public void clobberUpdateFutures(final ChunkMap chunkMap, final Executor executor, final CallbackInfo ci) {
throw new UnsupportedOperationException(); 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 ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.TicketStorage;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
@@ -15,8 +16,8 @@ abstract class ChunkMap$DistanceManagerMixin extends net.minecraft.server.level.
@Final @Final
ChunkMap field_17443; ChunkMap field_17443;
protected ChunkMap$DistanceManagerMixin(Executor executor, Executor executor2) { protected ChunkMap$DistanceManagerMixin(final TicketStorage p_394060_, final Executor p_140774_, final Executor p_140775_) {
super(executor, executor2); super(p_394060_, p_140774_, p_140775_);
} }
/** /**

View File

@@ -1,7 +1,7 @@
package ca.spottedleaf.moonrise.mixin.chunk_system; 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.MoonriseConstants;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; 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.ChunkSystemChunkMap;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; 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.StaticCache2D;
import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.world.level.ChunkPos; 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.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.LevelChunk; 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.chunk.storage.RegionStorageInfo;
import net.minecraft.world.level.entity.ChunkStatusUpdateListener; import net.minecraft.world.level.entity.ChunkStatusUpdateListener;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.LevelStorageSource;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
@@ -124,11 +124,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
) )
) )
private void constructor( private void constructor(
ServerLevel arg, LevelStorageSource.LevelStorageAccess arg2, DataFixer dataFixer, ServerLevel p_214836_, LevelStorageSource.LevelStorageAccess p_214837_, DataFixer p_214838_,
StructureTemplateManager arg3, Executor executor, BlockableEventLoop<Runnable> arg4, StructureTemplateManager p_214839_, Executor p_214840_, BlockableEventLoop p_214841_,
LightChunkGetter arg5, ChunkGenerator arg6, ChunkProgressListener arg7, LightChunkGetter p_214842_, ChunkGenerator p_214843_, ChunkProgressListener p_214844_,
ChunkStatusUpdateListener arg8, Supplier<DimensionDataStorage> supplier, int j, boolean bl, ChunkStatusUpdateListener p_214845_, Supplier p_214846_, TicketStorage p_394462_, int p_214847_,
final CallbackInfo ci) { boolean p_214848_, CallbackInfo ci) {
// intentionally destroy old chunk system hooks // intentionally destroy old chunk system hooks
this.updatingChunkMap = null; this.updatingChunkMap = null;
this.visibleChunkMap = null; this.visibleChunkMap = null;
@@ -143,7 +143,8 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
// Dummy impl for mods that try to loadAsync directly // Dummy impl for mods that try to loadAsync directly
this.worker = new IOWorker( this.worker = new IOWorker(
// copied from super call // 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 @Override
public boolean isOldChunkAround(final ChunkPos chunkPos, final int i) { public boolean isOldChunkAround(final ChunkPos chunkPos, final int i) {
@@ -499,7 +500,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
*/ */
@Overwrite @Overwrite
public int getPlayerViewDistance(final ServerPlayer player) { 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 * @author Spottedleaf
*/ */
@Redirect( @Redirect(
method = "forEachSpawnCandidateChunk", method = "collectSpawningChunks",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;" 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); 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) { 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, private void avoidApplyChunkTrackingViewInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer,
final ChunkTrackingView chunkTrackingView) { 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) { 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; 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.common.util.MoonriseConstants;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager; 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.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; 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 it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.DistanceManager; 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.ThrottlingChunkTaskDispatcher;
import net.minecraft.server.level.Ticket; 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.Mth;
import net.minecraft.util.SortedArraySet;
import net.minecraft.world.level.ChunkPos; 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.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; 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.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -31,13 +37,14 @@ import java.util.concurrent.Executor;
abstract class DistanceManagerMixin implements ChunkSystemDistanceManager { abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
@Shadow @Shadow
Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets; public LoadingChunkTracker loadingChunkTracker;
@Shadow @Shadow
private DistanceManager.ChunkTicketTracker ticketTracker; public SimulationChunkTracker simulationChunkTracker;
@Shadow @Shadow
private TickingTracker tickingTicketsTracker; @Final
private TicketStorage ticketStorage;
@Shadow @Shadow
private DistanceManager.PlayerTicketTracker playerTicketManager; 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 * @author Spottedleaf
*/ */
@Inject( @Inject(
@@ -74,15 +82,16 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
) )
) )
private void destroyFields(final CallbackInfo ci) { private void destroyFields(final CallbackInfo ci) {
this.tickets = null; this.loadingChunkTracker = null;
this.ticketTracker = null; this.simulationChunkTracker = null;
this.tickingTicketsTracker = null;
this.playerTicketManager = null; this.playerTicketManager = null;
this.chunksToUpdateFutures = null; this.chunksToUpdateFutures = null;
this.ticketDispatcher = null; this.ticketDispatcher = null;
this.ticketsToRelease = null; this.ticketsToRelease = null;
this.mainThreadExecutor = null; this.mainThreadExecutor = null;
this.simulationDistance = -1; this.simulationDistance = -1;
((ChunkSystemTicketStorage)this.ticketStorage).moonrise$setChunkMap(this.moonrise$getChunkMap());
} }
@Override @Override
@@ -90,15 +99,6 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager; 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 * @reason Route to new chunk system
* @author Spottedleaf * @author Spottedleaf
@@ -108,46 +108,6 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
return this.moonrise$getChunkHolderManager().processTicketUpdates(); 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 * @reason Remove old chunk system hooks
* @author Spottedleaf * @author Spottedleaf
@@ -170,11 +130,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
method = "addPlayer", method = "addPlayer",
at = @At( at = @At(
value = "INVOKE", 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, private void skipTickingTicketTrackerAdd(final TicketStorage instance, final Ticket ticket, final ChunkPos pos) {}
final ChunkPos chunkPos, final int i, final T object) {}
/** /**
* @reason Remove old chunk system hooks * @reason Remove old chunk system hooks
@@ -213,11 +172,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
method = "removePlayer", method = "removePlayer",
at = @At( at = @At(
value = "INVOKE", 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, private void skipTickingTicketTrackerRemove(final TicketStorage instance, final Ticket ticket, final ChunkPos pos) {}
final ChunkPos chunkPos, final int i, final T object) {}
/** /**
* @reason Remove old chunk system hooks * @reason Remove old chunk system hooks
@@ -268,8 +226,9 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public String getTicketDebugString(final long pos) { public int getChunkLevel(final long pos, final boolean simulation) {
return this.moonrise$getChunkHolderManager().getTicketDebugString(pos); 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); ((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 * @reason Route to new chunk system
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public String getDebugStatus() { public String getDebugStatus() {
return "No DistanceManager stats available"; return "N/A";
}
/**
* @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();
} }
} }

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

View File

@@ -25,7 +25,7 @@ abstract class EntityTickListMixin {
private Int2ObjectMap<Entity> passive; private Int2ObjectMap<Entity> passive;
@Unique @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 * @reason Initialise new fields and destroy old state

View File

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

View File

@@ -105,12 +105,12 @@ abstract class LevelChunkTicksMixin<T> implements ChunkSystemLevelChunkTicks, Se
* @author Spottedleaf * @author Spottedleaf
*/ */
@Inject( @Inject(
method = "save(JLjava/util/function/Function;)Lnet/minecraft/nbt/ListTag;", method = "pack",
at = @At( at = @At(
value = "HEAD" 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; 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) { 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; return false;
} }
@@ -238,9 +243,9 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
) )
) )
private void closeIOThreads(final CallbackInfo ci) { 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); 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) { if ((Object)this instanceof DedicatedServer) {
MoonriseCommon.haltExecutors(); 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.map.ConcurrentLong2ReferenceChainedHashTable;
import ca.spottedleaf.concurrentutil.util.Priority; import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.PlatformHooks; 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.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread; import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; 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.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; 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 ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkLevel; import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.ChunkResult; import net.minecraft.server.level.ChunkResult;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.FullChunkStatus; import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; 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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -59,9 +56,6 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
@Unique @Unique
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>(); private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
@Unique
private long chunksTicked;
@Override @Override
public final void moonrise$setFullChunk(final int chunkX, final int chunkZ, final LevelChunk chunk) { public final void moonrise$setFullChunk(final int chunkX, final int chunkZ, final LevelChunk chunk) {
final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ); final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
@@ -86,6 +80,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
completable::complete 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)) { if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ); ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
this.mainThreadProcessor.managedBlock(completable::isDone); 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 * @reason In the chunk system, spawn chunks will return only entity ticking chunks - we can elide the
* @author Spottedleaf * entity ticking range check.
*/
@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.
* @author Spottedleaf * @author Spottedleaf
*/ */
@Redirect( @Redirect(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V", method = "tickSpawningChunk",
at = @At( at = @At(
value = "INVOKE", 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; 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.io.datacontroller.PoiDataController;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevelReader; 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.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.level.entity.server.ServerEntityLookup;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader; import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; 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.core.RegistryAccess;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.RandomSequences; import net.minecraft.world.RandomSequences;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@@ -64,12 +61,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
@Mixin(ServerLevel.class) @Mixin(ServerLevel.class)
@@ -121,16 +116,16 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
private final NearbyPlayers nearbyPlayers = new NearbyPlayers((ServerLevel)(Object)this); private final NearbyPlayers nearbyPlayers = new NearbyPlayers((ServerLevel)(Object)this);
@Unique @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 @Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); private final ReferenceList<LevelChunk> loadedChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
@Unique @Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS); private final ReferenceList<LevelChunk> tickingChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
@Unique @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 * @reason Initialise fields / destroy entity manager state
@@ -334,17 +329,17 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
} }
@Override @Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getLoadedChunks() { public final ReferenceList<LevelChunk> moonrise$getLoadedChunks() {
return this.loadedChunks; return this.loadedChunks;
} }
@Override @Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks() { public final ReferenceList<LevelChunk> moonrise$getTickingChunks() {
return this.tickingChunks; return this.tickingChunks;
} }
@Override @Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks() { public final ReferenceList<LevelChunk> moonrise$getEntityTickingChunks() {
return this.entityTickingChunks; return this.entityTickingChunks;
} }
@@ -666,7 +661,7 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public boolean isPositionEntityTicking(BlockPos pos) { public boolean isPositionEntityTicking(final BlockPos pos) {
final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos)); final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos));
return chunkHolder != null && chunkHolder.isEntityTickingReady(); return chunkHolder != null && chunkHolder.isEntityTickingReady();
} }
@@ -676,7 +671,7 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public boolean isNaturalSpawningAllowed(final BlockPos pos) { public boolean areEntitiesActuallyLoadedAndTicking(final ChunkPos pos) {
final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos)); final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos));
return chunkHolder != null && chunkHolder.isEntityTickingReady(); return chunkHolder != null && chunkHolder.isEntityTickingReady();
} }
@@ -685,8 +680,14 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
* @reason Redirect to chunk system * @reason Redirect to chunk system
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Redirect(
public boolean isNaturalSpawningAllowed(final ChunkPos pos) { 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)); final NewChunkHolder chunkHolder = this.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(CoordinateUtils.getChunkKey(pos));
return chunkHolder != null && chunkHolder.isEntityTickingReady(); return chunkHolder != null && chunkHolder.isEntityTickingReady();
} }

View File

@@ -49,8 +49,7 @@ abstract class SortedArraySetMixin<T> extends AbstractSet<T> implements ChunkSys
if (i >= len) { if (i >= len) {
return false; return false;
} }
if (!filter.test(backingArray[i])) { if (!filter.test(backingArray[i++])) {
++i;
continue; continue;
} }
break; 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 // 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) { for (; i < len; ++i) {
final T curr = backingArray[i]; final T curr = backingArray[i];

View File

@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.mixin.chunk_system; package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket; 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.Ticket;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import org.spongepowered.asm.mixin.Final; 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.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import java.util.Comparator;
@Mixin(Ticket.class) @Mixin(Ticket.class)
abstract class TicketMixin<T> implements ChunkSystemTicket<T>, Comparable<Ticket<?>> { abstract class TicketMixin<T> implements ChunkSystemTicket<T>, Comparable<Ticket> {
@Shadow @Shadow
@Final @Final
private TicketType<T> type; private TicketType type;
@Shadow @Shadow
@Final @Final
private int ticketLevel; private int ticketLevel;
@Shadow @Shadow
@Final private long ticksLeft;
public T key;
@Unique @Unique
private long removeDelay; private T identifier;
@Override @Override
public final long moonrise$getRemoveDelay() { public final long moonrise$getRemoveDelay() {
return this.removeDelay; return this.ticksLeft;
} }
@Override @Override
public final void moonrise$setRemoveDelay(final long removeDelay) { 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 * @author Spottedleaf
*/ */
@Overwrite @Overwrite
@Override @Override
public String toString() { 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;
} }
/** @Override
* @reason Remove old chunk system hook public final int compareTo(final Ticket ticket) {
* @author Spottedleaf final int levelCompare = Integer.compare(this.ticketLevel, ((TicketMixin<?>)(Object)ticket).ticketLevel);
*/ if (levelCompare != 0) {
@Overwrite return levelCompare;
public void setCreatedTick(final long tickCreated) { }
throw new UnsupportedOperationException();
}
/** final int typeCompare = Long.compare(
* @reason Remove old chunk system hook ((ChunkSystemTicketType<T>)(Object)this.type).moonrise$getId(),
* @author Spottedleaf ((ChunkSystemTicketType<?>)(Object)((TicketMixin<?>)(Object)ticket).type).moonrise$getId()
*/ );
@Overwrite if (typeCompare != 0) {
public boolean timedOut(final long currentTick) { return typeCompare;
throw new UnsupportedOperationException(); }
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,260 @@
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.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() {
((ChunkSystemServerLevel)this.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/Predicate;Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;)V"
)
)
private void avoidRemovingTicketsOnShutdown(final TicketStorage instance,
final Predicate<Ticket> predicate,
final Long2ObjectOpenHashMap<List<Ticket>> tickets) {}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void removeTicketIf(final Predicate<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,69 @@
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.Mixin;
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> {
@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);
}
}

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.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; 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.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.ChunkTickDistanceManager;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import com.llamalad7.mixinextras.sugar.Local; 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.core.SectionPos;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Final; import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 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 * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) { public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
if (((ChunkTickDistanceManager)this.distanceManager).moonrise$hasAnyNearbyNarrow(pos.x, pos.z)) {
return true;
}
return this.anyPlayerCloseEnoughForSpawningInternal(pos); return this.anyPlayerCloseEnoughForSpawningInternal(pos);
} }
@@ -152,4 +165,111 @@ abstract class ChunkMapMixin {
return ret == null ? new ArrayList<>() : ret; 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.core.SectionPos;
import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.TriState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -21,20 +22,24 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
abstract class DistanceManagerMixin implements ChunkTickDistanceManager { abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
@Shadow @Shadow
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter; public DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
@Unique @Unique
private final PositionCountingAreaMap<ServerPlayer> spawnChunkTracker = new PositionCountingAreaMap<>(); private final PositionCountingAreaMap<ServerPlayer> spawnChunkTracker = new PositionCountingAreaMap<>();
@Unique
private final PositionCountingAreaMap<ServerPlayer> narrowSpawnChunkTracker = new PositionCountingAreaMap<>();
@Override @Override
public final void moonrise$addPlayer(final ServerPlayer player, final SectionPos pos) { 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.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 @Override
public final void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos) { public final void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos) {
this.spawnChunkTracker.remove(player); this.spawnChunkTracker.remove(player);
this.narrowSpawnChunkTracker.remove(player);
} }
@Override @Override
@@ -43,11 +48,18 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
final boolean oldIgnore, final boolean newIgnore) { final boolean oldIgnore, final boolean newIgnore) {
if (newIgnore) { if (newIgnore) {
this.spawnChunkTracker.remove(player); this.spawnChunkTracker.remove(player);
this.narrowSpawnChunkTracker.remove(player);
} else { } else {
this.spawnChunkTracker.addOrUpdate(player, newPos.x(), newPos.z(), ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); 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 * @reason Destroy natural spawning tracker field to prevent it from being used
* @author Spottedleaf * @author Spottedleaf
@@ -104,8 +116,11 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
* @author Spottedleaf * @author Spottedleaf
*/ */
@Overwrite @Overwrite
public boolean hasPlayersNearby(final long pos) { public TriState hasPlayersNearby(final long pos) {
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(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; package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
import ca.spottedleaf.moonrise.common.list.ReferenceList; 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.common.util.SimpleThreadUnsafeRandom;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData; 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.server.ChunkSystemMinecraftServer;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource; 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.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
@Mixin(ServerChunkCache.class) @Mixin(ServerChunkCache.class)
abstract class ServerChunkCacheMixin extends ChunkSource { abstract class ServerChunkCacheMixin extends ChunkSource {
@@ -41,69 +37,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
@Unique @Unique
private final SimpleThreadUnsafeRandom shuffleRandom = new SimpleThreadUnsafeRandom(0L); 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) * @reason Use random implementation which does not use CAS and has a faster nextInt(int)
* function * function
* @author Spottedleaf * @author Spottedleaf
*/ */
@Redirect( @Redirect(
method = "tickChunks()V", method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;J)V",
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V" 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()); this.shuffleRandom.setSeed(randomSource.nextLong());
Util.shuffle(list, this.shuffleRandom); 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 { abstract class ServerLevelMixin implements ChunkTickServerLevel {
@Unique @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 @Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> playerTickingChunks = new ReferenceList<>(EMPTY_PLAYER_CHUNK_HOLDERS); private final ReferenceList<LevelChunk> playerTickingChunks = new ReferenceList<>(EMPTY_LEVEL_CHUNKS);
@Unique @Unique
private final Long2IntOpenHashMap playerTickingRequests = new Long2IntOpenHashMap(); private final Long2IntOpenHashMap playerTickingRequests = new Long2IntOpenHashMap();
@Override @Override
public final ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks() { public final ReferenceList<LevelChunk> moonrise$getPlayerTickingChunks() {
return this.playerTickingChunks; return this.playerTickingChunks;
} }
@@ -39,12 +39,12 @@ abstract class ServerLevelMixin implements ChunkTickServerLevel {
return; return;
} }
this.playerTickingChunks.add(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()); this.playerTickingChunks.add(chunk);
} }
@Override @Override
public final void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk) { public final void moonrise$removeChunkForPlayerTicking(final LevelChunk chunk) {
this.playerTickingChunks.remove(((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()); this.playerTickingChunks.remove(chunk);
} }
@Override @Override
@@ -65,9 +65,7 @@ abstract class ServerLevelMixin implements ChunkTickServerLevel {
return; return;
} }
this.playerTickingChunks.add( this.playerTickingChunks.add((LevelChunk)chunkHolder.getCurrentChunk());
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
);
} }
@Override @Override
@@ -93,8 +91,6 @@ abstract class ServerLevelMixin implements ChunkTickServerLevel {
return; return;
} }
this.playerTickingChunks.remove( this.playerTickingChunks.remove((LevelChunk)chunkHolder.getCurrentChunk());
((ChunkSystemLevelChunk)(LevelChunk)chunkHolder.getCurrentChunk()).moonrise$getChunkAndHolder()
);
} }
} }

View File

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

View File

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

View File

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

View File

@@ -24,8 +24,8 @@ abstract class AcquirePoiMixin {
*/ */
@Redirect( @Redirect(
method = { method = {
"method_46885", "lambda$create$8",
"*(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" "method_46885"
}, },
at = @At( 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;", 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 ordinal = 0
) )
) )
private static Stream<Pair<Holder<PoiType>, BlockPos>> aaa(PoiManager poiManager, Predicate<Holder<PoiType>> predicate, private static Stream<Pair<Holder<PoiType>, BlockPos>> useLimitedSearch(PoiManager poiManager, Predicate<Holder<PoiType>> predicate,
Predicate<BlockPos> predicate2, BlockPos blockPos, int i, Predicate<BlockPos> predicate2, BlockPos blockPos, int i,
PoiManager.Occupancy occup) { PoiManager.Occupancy occup) {
final List<Pair<Holder<PoiType>, BlockPos>> ret = new ArrayList<>(); final List<Pair<Holder<PoiType>, BlockPos>> ret = new ArrayList<>();
PoiAccess.findNearestPoiPositions( PoiAccess.findNearestPoiPositions(

View File

@@ -17,7 +17,7 @@ interface ServerAddressResolverMixin {
@Redirect( @Redirect(
method = { method = {
"method_36903", "method_36903",
"*(Lnet/minecraft/client/multiplayer/resolver/ServerAddress;)Ljava/util/Optional;" "lambda$static$0"
}, },
at = @At( at = @At(
value = "INVOKE", value = "INVOKE",

View File

@@ -49,10 +49,10 @@ import java.util.function.Supplier;
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider { abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
@Shadow @Shadow
private ConsecutiveExecutor consecutiveExecutor; public ConsecutiveExecutor consecutiveExecutor;
@Shadow @Shadow
private ChunkTaskDispatcher taskDispatcher; public ChunkTaskDispatcher taskDispatcher;
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) { public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
super(chunkProvider, hasBlockLight, hasSkyLight); super(chunkProvider, hasBlockLight, hasSkyLight);
@@ -86,10 +86,10 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
final Long ticketId = Long.valueOf(this.chunkWorkCounter.getAndIncrement()); final Long ticketId = Long.valueOf(this.chunkWorkCounter.getAndIncrement());
final ChunkPos pos = new ChunkPos(chunkX, chunkZ); 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(() -> { 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 ChunkPos pos = iterator.next();
final Long id = ChunkTaskScheduler.getNextChunkRelightId(); 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); ticketIds.put(pos, id);
final ChunkAccess chunk = (ChunkAccess)world.getChunkSource().getChunkForLighting(pos.x, pos.z); 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 // cannot relight this chunk
iterator.remove(); iterator.remove();
ticketIds.remove(pos); 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; continue;
} }
} }
@@ -160,9 +160,9 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
} }
for (final Map.Entry<ChunkPos, Long> entry : ticketIds.entrySet()) { for (final Map.Entry<ChunkPos, Long> entry : ticketIds.entrySet()) {
world.getChunkSource().removeRegionTicket( ((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.removeTicketAtLevel(
ChunkTaskScheduler.CHUNK_RELIGHT, entry.getKey(), 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", method = "parse",
at = @At( at = @At(
value = "INVOKE", 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 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) { @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; return status.isOrAfter(ChunkStatus.LIGHT) && starlightCorrect;
} }
@@ -84,17 +84,17 @@ abstract class SerializableChunkDataMixin {
) )
private static SerializableChunkData.SectionData readStarlightState(final int y, final LevelChunkSection chunkSection, private static SerializableChunkData.SectionData readStarlightState(final int y, final LevelChunkSection chunkSection,
final DataLayer blockLight, final DataLayer skyLight, 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( final SerializableChunkData.SectionData ret = new SerializableChunkData.SectionData(
y, chunkSection, blockLight, skyLight y, chunkSection, blockLight, skyLight
); );
if (sectionData.contains(SaveUtil.BLOCKLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) { if (sectionData.contains(SaveUtil.BLOCKLIGHT_STATE_TAG)) {
((StarlightSectionData)(Object)ret).starlight$setBlockLightState(sectionData.getInt(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)) { if (sectionData.contains(SaveUtil.SKYLIGHT_STATE_TAG)) {
((StarlightSectionData)(Object)ret).starlight$setSkyLightState(sectionData.getInt(SaveUtil.SKYLIGHT_STATE_TAG)); ((StarlightSectionData)(Object)ret).starlight$setSkyLightState(sectionData.getIntOr(SaveUtil.SKYLIGHT_STATE_TAG, 0));
} }
return ret; return ret;

View File

@@ -19,8 +19,7 @@ public final class ChunkSystemConverters {
} }
private static int getDataVersion(final CompoundTag data, final int dfl) { private static int getDataVersion(final CompoundTag data, final int dfl) {
return !data.contains(SharedConstants.DATA_VERSION_TAG, Tag.TAG_ANY_NUMERIC) return data.getIntOr(SharedConstants.DATA_VERSION_TAG, dfl);
? dfl : data.getInt(SharedConstants.DATA_VERSION_TAG);
} }
public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) { public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) {

View File

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

View File

@@ -48,7 +48,7 @@ public final class EntityDataController extends MoonriseRegionFileIO.RegionDataC
} }
private static void checkPosition(final ChunkPos pos, final CompoundTag nbt) { 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)) { if (nbtPos != null && !pos.equals(nbtPos)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString() "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; package ca.spottedleaf.moonrise.patches.chunk_system.level;
import ca.spottedleaf.concurrentutil.util.Priority; 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.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; 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.ChunkHolder;
import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -56,9 +58,9 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
public NearbyPlayers moonrise$getNearbyPlayers(); 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; 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 interface ChunkSystemLevelChunk {
public boolean moonrise$isPostProcessingDone(); 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

@@ -76,7 +76,7 @@ public final class ChunkEntitySlices {
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) { public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
// TODO check this and below on update for format changes // TODO check this and below on update for format changes
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList()); return EntityType.loadEntitiesRecursive(compoundTag.getListOrEmpty("Entities"), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
} }
// Paper start - rewrite chunk system // Paper start - rewrite chunk system
@@ -84,12 +84,12 @@ public final class ChunkEntitySlices {
if (from == null) { if (from == null) {
return; return;
} }
final ListTag entitiesFrom = from.getList("Entities", Tag.TAG_COMPOUND); final ListTag entitiesFrom = from.getListOrEmpty("Entities");
if (entitiesFrom == null || entitiesFrom.isEmpty()) { if (entitiesFrom == null || entitiesFrom.isEmpty()) {
return; 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 into.put("Entities", entitiesInto); // this is in case into doesn't have any entities
entitiesInto.addAll(0, entitiesFrom); entitiesInto.addAll(0, entitiesFrom);
} }
@@ -112,7 +112,7 @@ public final class ChunkEntitySlices {
} }
final CompoundTag ret = NbtUtils.addCurrentDataVersion(new CompoundTag()); final CompoundTag ret = NbtUtils.addCurrentDataVersion(new CompoundTag());
ret.put("Entities", entitiesTag); ret.put("Entities", entitiesTag);
EntityStorage.writeChunkPos(ret, chunkPos); ret.store("Position", ChunkPos.CODEC, chunkPos);
return !force && entitiesTag.isEmpty() ? null : ret; return !force && entitiesTag.isEmpty() ? null : ret;
} }
@@ -291,21 +291,12 @@ public final class ChunkEntitySlices {
} }
public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) { 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); 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, public boolean getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) { 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) {
return this.allEntities.getEntitiesLimited(except, box, into, predicate, maxCount); return this.allEntities.getEntitiesLimited(except, box, into, predicate, maxCount);
} }
@@ -319,7 +310,7 @@ public final class ChunkEntitySlices {
} }
public <T extends Entity> boolean getEntities(final EntityType<?> type, final AABB box, final List<? super T> into, 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); final EntityCollectionBySection byType = this.entitiesByType.get(type);
if (byType != null) { if (byType != null) {
@@ -356,21 +347,21 @@ public final class ChunkEntitySlices {
final Predicate<? super T> predicate) { final Predicate<? super T> predicate) {
EntityCollectionBySection collection = this.entitiesByClass.get(clazz); EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
if (collection != null) { if (collection != null) {
collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate); collection.getEntities(except, box, (List)into, (Predicate)predicate);
} else { } else {
this.entitiesByClass.put(clazz, collection = this.initClass(clazz)); 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, 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); EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
if (collection != null) { 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 { } else {
this.entitiesByClass.put(clazz, collection = this.initClass(clazz)); 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 +565,5 @@ public final class ChunkEntitySlices {
return false; 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() { public int getEntityCount() {
synchronized (this.accessibleEntities) { synchronized (this.accessibleEntities) {
return this.accessibleEntities.size(); return this.accessibleEntities.size();
@@ -209,7 +213,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
@Override @Override
public void get(final AABB box, final Consumer<Entity> action) { public void get(final AABB box, final Consumer<Entity> action) {
List<Entity> entities = new ArrayList<>(); 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) { for (int i = 0, len = entities.size(); i < len; ++i) {
action.accept(entities.get(i)); action.accept(entities.get(i));
} }
@@ -218,7 +222,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
@Override @Override
public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final AbortableIterationConsumer<U> action) { public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final AbortableIterationConsumer<U> action) {
List<Entity> entities = new ArrayList<>(); 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) { for (int i = 0, len = entities.size(); i < len; ++i) {
final U casted = filter.tryCast(entities.get(i)); final U casted = filter.tryCast(entities.get(i));
if (casted != null && action.accept(casted).shouldAbort()) { if (casted != null && action.accept(casted).shouldAbort()) {
@@ -560,45 +564,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
return slices; 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) { 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 minChunkX = (Mth.floor(box.minX) - 2) >> 4;
final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
@@ -759,48 +724,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
//////// Limited //////// //////// 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, public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate,
final int maxCount) { final int maxCount) {
final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;

View File

@@ -4,7 +4,6 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.ReferenceList; import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils; import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread; 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.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
@@ -111,6 +110,6 @@ public final class ServerEntityLookup extends EntityLookup {
@Override @Override
protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) { protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) {
return ChunkSystem.screenEntity(this.serverWorld, entity, fromDisk, event); return PlatformHooks.get().screenEntity(this.serverWorld, entity, fromDisk, event);
} }
} }

View File

@@ -159,7 +159,7 @@ public final class PoiChunk {
final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, world.registryAccess()); 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()) { if (sections.isEmpty()) {
// nothing to parse // nothing to parse
@@ -176,7 +176,7 @@ public final class PoiChunk {
continue; 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 DataResult<PoiSection.Packed> deserializeResult = PoiSection.Packed.CODEC.parse(registryOps, section);
final int finalSectionY = sectionY; final int finalSectionY = sectionY;
final PoiSection.Packed packed = deserializeResult.resultOrPartial((final String description) -> { 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.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; 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.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration; import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
@@ -45,11 +46,8 @@ import java.util.function.Function;
public final class RegionizedPlayerChunkLoader { public final class RegionizedPlayerChunkLoader {
public static final TicketType<Long> PLAYER_TICKET = TicketType.create("chunk_system:player_ticket", Long::compareTo); public static final TicketType PLAYER_TICKET = ChunkSystemTicketType.create("chunk_system:player_ticket", Long::compareTo);
public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5 * 20); public static final TicketType PLAYER_TICKET_DELAYED = ChunkSystemTicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5L * 20L);
public static final int MIN_VIEW_DISTANCE = 2;
public static final int MAX_VIEW_DISTANCE = 32;
public static final int GENERATED_TICKET_LEVEL = ChunkHolderManager.FULL_LOADED_TICKET_LEVEL; public static final int GENERATED_TICKET_LEVEL = ChunkHolderManager.FULL_LOADED_TICKET_LEVEL;
public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY); public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
@@ -685,8 +683,7 @@ public final class RegionizedPlayerChunkLoader {
} }
this.pushDelayedTicketOp( this.pushDelayedTicketOp(
ChunkHolderManager.TicketOperation.addOp( ChunkHolderManager.TicketOperation.addOp(
chunk, chunk, PLAYER_TICKET, LOADED_TICKET_LEVEL, this.idBoxed
PLAYER_TICKET, LOADED_TICKET_LEVEL, this.idBoxed
) )
); );
chunks.add(chunk); chunks.add(chunk);

View File

@@ -7,7 +7,6 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils; import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread; import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil; 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.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices; import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
@@ -17,9 +16,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.ChunkProgressionTask;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.GenericDataLoadTask; 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.ChunkSystemTicket;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ChunkSystemSortedArraySet; import ca.spottedleaf.moonrise.patches.chunk_system.util.ChunkSystemSortedArraySet;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; 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.Long2ByteLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ByteMap; import it.unimi.dsi.fastutil.longs.Long2ByteMap;
import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntMap;
@@ -66,13 +67,12 @@ public final class ChunkHolderManager {
public static final int ENTITY_TICKING_TICKET_LEVEL = ChunkLevel.ENTITY_TICKING_LEVEL; 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 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 NO_TIMEOUT_MARKER = Long.MIN_VALUE;
private static final long PROBE_MARKER = Long.MIN_VALUE + 1;
public final ReentrantAreaLock ticketLockArea; 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<>(); private final ConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> sectionToChunkToExpireCount = new ConcurrentLong2ReferenceChainedHashTable<>();
final ChunkUnloadQueue unloadQueue; final ChunkUnloadQueue unloadQueue;
@@ -103,6 +103,8 @@ public final class ChunkHolderManager {
return Long.compare(coord1, coord2); return Long.compare(coord1, coord2);
}); });
private final ConcurrentLong2ReferenceChainedHashTable<Long2IntOpenHashMap> ticketCounters = new ConcurrentLong2ReferenceChainedHashTable<>();
public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) { public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) {
this.world = world; this.world = world;
this.taskScheduler = taskScheduler; this.taskScheduler = taskScheduler;
@@ -164,7 +166,6 @@ public final class ChunkHolderManager {
return this.chunkHolders.size(); return this.chunkHolders.size();
} }
// TODO replace the need for this, specifically: optimise ServerChunkCache#tickChunks
public Iterable<ChunkHolder> getOldChunkHoldersIterable() { public Iterable<ChunkHolder> getOldChunkHoldersIterable() {
return new Iterable<ChunkHolder>() { return new Iterable<ChunkHolder>() {
@Override @Override
@@ -219,6 +220,8 @@ public final class ChunkHolderManager {
LOGGER.error("Failed to close '" + type.name() + "' regionfile cache for world '" + WorldUtil.getWorldName(this.world) + "'", ex); 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) { void ensureInAutosave(final NewChunkHolder holder) {
@@ -410,7 +413,7 @@ public final class ChunkHolderManager {
public String getTicketDebugString(final long coordinate) { public String getTicketDebugString(final long coordinate) {
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(coordinate), CoordinateUtils.getChunkZ(coordinate)); final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(coordinate), CoordinateUtils.getChunkZ(coordinate));
try { 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"; return tickets != null ? tickets.first().toString() : "no_ticket";
} finally { } finally {
@@ -420,8 +423,40 @@ public final class ChunkHolderManager {
} }
} }
public Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> getTicketsCopy() { public boolean hasTickets() {
final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> ret = new Long2ObjectOpenHashMap<>(); 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 Long2ObjectOpenHashMap<LongArrayList> sections = new Long2ObjectOpenHashMap<>();
final int sectionShift = this.taskScheduler.getChunkSystemLockShift(); final int sectionShift = this.taskScheduler.getChunkSystemLockShift();
for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) { for (final PrimitiveIterator.OfLong iterator = this.tickets.keyIterator(); iterator.hasNext();) {
@@ -450,12 +485,12 @@ public final class ChunkHolderManager {
try { try {
for (final LongIterator iterator2 = coordinates.iterator(); iterator2.hasNext();) { for (final LongIterator iterator2 = coordinates.iterator(); iterator2.hasNext();) {
final long coord = iterator2.nextLong(); final long coord = iterator2.nextLong();
final SortedArraySet<Ticket<?>> tickets = this.tickets.get(coord); final SortedArraySet<Ticket> tickets = this.tickets.get(coord);
if (tickets == null) { if (tickets == null) {
// removed before we acquired lock // removed before we acquired lock
continue; continue;
} }
ret.put(coord, ((ChunkSystemSortedArraySet<Ticket<?>>)tickets).moonrise$copy()); ret.put(coord, ((ChunkSystemSortedArraySet<Ticket>)tickets).moonrise$copy());
} }
} finally { } finally {
this.ticketLockArea.unlock(ticketLock); this.ticketLockArea.unlock(ticketLock);
@@ -473,16 +508,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; 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) { final T identifier) {
return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkPos), level, 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) { final T identifier) {
return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkX, chunkZ), level, identifier); return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkX, chunkZ), level, identifier);
} }
@@ -523,29 +558,29 @@ public final class ChunkHolderManager {
// supposed to return true if the ticket was added and did not replace another // 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 // 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); 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) { <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; final long removeDelay = type.timeout() <= 0 ? NO_TIMEOUT_MARKER : type.timeout();
if (level > MAX_TICKET_LEVEL) { if (level > MAX_TICKET_LEVEL) {
return false; return false;
} }
final int chunkX = CoordinateUtils.getChunkX(chunk); final int chunkX = CoordinateUtils.getChunkX(chunk);
final int chunkZ = CoordinateUtils.getChunkZ(chunk); final int chunkZ = CoordinateUtils.getChunkZ(chunk);
final Ticket<T> ticket = new Ticket<>(type, level, identifier); final Ticket ticket = new Ticket(type, level, removeDelay);
((ChunkSystemTicket<T>)(Object)ticket).moonrise$setRemoveDelay(removeDelay); ((ChunkSystemTicket<T>)(Object)ticket).moonrise$setIdentifier(identifier);
final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null; final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null;
try { try {
final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> { final SortedArraySet<Ticket> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> {
return SortedArraySet.create(4); return (SortedArraySet)SortedArraySet.create(4);
}); });
final int levelBefore = getTicketLevelAt(ticketsAtChunk); 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); final int levelAfter = getTicketLevelAt(ticketsAtChunk);
if (current != ticket) { if (current != ticket) {
@@ -562,6 +597,7 @@ public final class ChunkHolderManager {
if (removeDelay != NO_TIMEOUT_MARKER) { if (removeDelay != NO_TIMEOUT_MARKER) {
this.addExpireCount(chunkX, chunkZ); this.addExpireCount(chunkX, chunkZ);
} }
this.addTicketCounter(type, chunk);
} }
if (levelBefore != levelAfter) { if (levelBefore != levelAfter) {
@@ -576,36 +612,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); 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); 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); 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) { if (level > MAX_TICKET_LEVEL) {
return false; return false;
} }
final int chunkX = CoordinateUtils.getChunkX(chunk); final int chunkX = CoordinateUtils.getChunkX(chunk);
final int chunkZ = CoordinateUtils.getChunkZ(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; final ReentrantAreaLock.Node ticketLock = lock ? this.ticketLockArea.lock(chunkX, chunkZ) : null;
try { try {
final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.get(chunk); final SortedArraySet<Ticket> ticketsAtChunk = this.tickets.get(chunk);
if (ticketsAtChunk == null) { if (ticketsAtChunk == null) {
return false; return false;
} }
final int oldLevel = getTicketLevelAt(ticketsAtChunk); 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) { if (ticket == null) {
return false; return false;
@@ -614,8 +699,7 @@ public final class ChunkHolderManager {
final int newLevel = getTicketLevelAt(ticketsAtChunk); final int newLevel = getTicketLevelAt(ticketsAtChunk);
// we should not change the ticket levels while the target region may be ticking // we should not change the ticket levels while the target region may be ticking
if (oldLevel != newLevel) { if (oldLevel != newLevel) {
final Ticket<ChunkPos> unknownTicket = new Ticket<>(TicketType.UNKNOWN, level, new ChunkPos(chunk)); final Ticket unknownTicket = new Ticket(TicketType.UNKNOWN, level);
((ChunkSystemTicket<ChunkPos>)(Object)unknownTicket).moonrise$setRemoveDelay(Math.max(1, TicketType.UNKNOWN.timeout));
if (ticketsAtChunk.add(unknownTicket)) { if (ticketsAtChunk.add(unknownTicket)) {
this.addExpireCount(chunkX, chunkZ); this.addExpireCount(chunkX, chunkZ);
} else { } else {
@@ -628,6 +712,8 @@ public final class ChunkHolderManager {
this.removeExpireCount(chunkX, chunkZ); this.removeExpireCount(chunkX, chunkZ);
} }
this.removeTicketCounter(type, chunk);
return true; return true;
} finally { } finally {
if (ticketLock != null) { if (ticketLock != null) {
@@ -637,8 +723,8 @@ public final class ChunkHolderManager {
} }
// atomic with respect to all add/remove/addandremove ticket calls for the given chunk // 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, public <T, V> void addAndRemoveTickets(final long chunk, final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) { final TicketType removeType, final int removeLevel, final V removeIdentifier) {
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(chunk), CoordinateUtils.getChunkZ(chunk)); final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(chunk), CoordinateUtils.getChunkZ(chunk));
try { try {
this.addTicketAtLevel(addType, chunk, addLevel, addIdentifier, false); this.addTicketAtLevel(addType, chunk, addLevel, addIdentifier, false);
@@ -649,8 +735,8 @@ public final class ChunkHolderManager {
} }
// atomic with respect to all add/remove/addandremove ticket calls for the given chunk // 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, public <T, V> boolean addIfRemovedTicket(final long chunk, final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) { final TicketType removeType, final int removeLevel, final V removeIdentifier) {
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(chunk), CoordinateUtils.getChunkZ(chunk)); final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(CoordinateUtils.getChunkX(chunk), CoordinateUtils.getChunkZ(chunk));
try { try {
if (this.removeTicketAtLevel(removeType, chunk, removeLevel, removeIdentifier, false)) { if (this.removeTicketAtLevel(removeType, chunk, removeLevel, removeIdentifier, false)) {
@@ -663,7 +749,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) { if (ticketLevel > MAX_TICKET_LEVEL) {
return; return;
} }
@@ -709,7 +795,7 @@ public final class ChunkHolderManager {
final int sectionShift = ((ChunkSystemServerLevel)this.world).moonrise$getRegionChunkShift(); 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(); long removeDelay = ((ChunkSystemTicket<?>)(Object)ticket).moonrise$getRemoveDelay();
if (removeDelay == NO_TIMEOUT_MARKER) { if (removeDelay == NO_TIMEOUT_MARKER) {
return false; return false;
@@ -745,7 +831,7 @@ public final class ChunkHolderManager {
final long chunkKey = entry.getLongKey(); final long chunkKey = entry.getLongKey();
final int expireCount = entry.getIntValue(); 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 levelBefore = getTicketLevelAt(tickets);
final int sizeBefore = tickets.size(); final int sizeBefore = tickets.size();
@@ -816,7 +902,7 @@ public final class ChunkHolderManager {
private NewChunkHolder createChunkHolder(final long position) { private NewChunkHolder createChunkHolder(final long position) {
final NewChunkHolder ret = new NewChunkHolder(this.world, CoordinateUtils.getChunkX(position), CoordinateUtils.getChunkZ(position), this.taskScheduler); 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; return ret;
} }
@@ -1024,7 +1110,7 @@ public final class ChunkHolderManager {
private void removeChunkHolder(final NewChunkHolder holder) { private void removeChunkHolder(final NewChunkHolder holder) {
holder.onUnload(); holder.onUnload();
this.autoSaveQueue.remove(holder); 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)); this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ));
} }
@@ -1163,7 +1249,7 @@ public final class ChunkHolderManager {
this.removeChunkHolder(holder); this.removeChunkHolder(holder);
} else { } else {
// add cooldown so the next unload check is not immediately next tick // 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 { } finally {
@@ -1187,42 +1273,42 @@ public final class ChunkHolderManager {
public static record TicketOperation<T, V> ( public static record TicketOperation<T, V> (
TicketOperationType op, long chunkCoord, TicketOperationType op, long chunkCoord,
TicketType<T> ticketType, int ticketLevel, T identifier, TicketType ticketType, int ticketLevel, T identifier,
TicketType<V> ticketType2, int ticketLevel2, V identifier2 TicketType ticketType2, int ticketLevel2, V identifier2
) { ) {
private TicketOperation(TicketOperationType op, long chunkCoord, 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); 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); 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); 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); 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); 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); 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); return new TicketOperation<>(TicketOperationType.REMOVE, chunk, type, ticketLevel, identifier);
} }
public static <T, V> TicketOperation<T, V> addIfRemovedOp(final long chunk, public static <T, V> TicketOperation<T, V> addIfRemovedOp(final long chunk,
final TicketType<T> addType, final int addLevel, final T addIdentifier, final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) { final TicketType removeType, final int removeLevel, final V removeIdentifier) {
return new TicketOperation<>( return new TicketOperation<>(
TicketOperationType.ADD_IF_REMOVED, chunk, addType, addLevel, addIdentifier, TicketOperationType.ADD_IF_REMOVED, chunk, addType, addLevel, addIdentifier,
removeType, removeLevel, removeIdentifier removeType, removeLevel, removeIdentifier
@@ -1230,8 +1316,8 @@ public final class ChunkHolderManager {
} }
public static <T, V> TicketOperation<T, V> addAndRemove(final long chunk, public static <T, V> TicketOperation<T, V> addAndRemove(final long chunk,
final TicketType<T> addType, final int addLevel, final T addIdentifier, final TicketType addType, final int addLevel, final T addIdentifier,
final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) { final TicketType removeType, final int removeLevel, final V removeIdentifier) {
return new TicketOperation<>( return new TicketOperation<>(
TicketOperationType.ADD_AND_REMOVE, chunk, addType, addLevel, addIdentifier, TicketOperationType.ADD_AND_REMOVE, chunk, addType, addLevel, addIdentifier,
removeType, removeLevel, removeIdentifier removeType, removeLevel, removeIdentifier
@@ -1390,11 +1476,11 @@ public final class ChunkHolderManager {
final JsonArray allTicketsJson = new JsonArray(); final JsonArray allTicketsJson = new JsonArray();
ret.add("tickets", allTicketsJson); 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();) { iterator.hasNext();) {
final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket<?>>> coordinateTickets = iterator.next(); final ConcurrentLong2ReferenceChainedHashTable.TableEntry<SortedArraySet<Ticket>> coordinateTickets = iterator.next();
final long coordinate = coordinateTickets.getKey(); final long coordinate = coordinateTickets.getKey();
final SortedArraySet<Ticket<?>> tickets = coordinateTickets.getValue(); final SortedArraySet<Ticket> tickets = coordinateTickets.getValue();
final JsonObject coordinateJson = new JsonObject(); final JsonObject coordinateJson = new JsonObject();
allTicketsJson.add(coordinateJson); allTicketsJson.add(coordinateJson);
@@ -1409,17 +1495,17 @@ public final class ChunkHolderManager {
// directly over the set using the iterator // 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 // 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 // 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) { if (ticketUncasted == null) {
continue; continue;
} }
final Ticket<?> ticket = (Ticket<?>)ticketUncasted; final Ticket ticket = (Ticket)ticketUncasted;
final JsonObject ticketSerialized = new JsonObject(); final JsonObject ticketSerialized = new JsonObject();
ticketsSerialized.add(ticketSerialized); ticketsSerialized.add(ticketSerialized);
ticketSerialized.addProperty("type", ticket.getType().toString()); ticketSerialized.addProperty("type", ticket.getType().toString());
ticketSerialized.addProperty("level", Integer.valueOf(ticket.getTicketLevel())); 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())); 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.scheduling.task.ChunkUpgradeGenericStatusTask;
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer; 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.status.ChunkSystemChunkStep;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration; import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -73,35 +74,35 @@ public final class ChunkTaskScheduler {
LOGGER.info("Chunk system is using population gen parallelism: " + useParallelGen); 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(); private static final AtomicLong CHUNK_LOAD_IDS = new AtomicLong();
public static Long getNextChunkLoadId() { public static Long getNextChunkLoadId() {
return Long.valueOf(CHUNK_LOAD_IDS.getAndIncrement()); 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(); private static final AtomicLong NON_FULL_CHUNK_LOAD_IDS = new AtomicLong();
public static Long getNextNonFullLoadId() { public static Long getNextNonFullLoadId() {
return Long.valueOf(NON_FULL_CHUNK_LOAD_IDS.getAndIncrement()); 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(); private static final AtomicLong ENTITY_LOAD_IDS = new AtomicLong();
public static Long getNextEntityLoadId() { public static Long getNextEntityLoadId() {
return Long.valueOf(ENTITY_LOAD_IDS.getAndIncrement()); 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(); private static final AtomicLong POI_LOAD_IDS = new AtomicLong();
public static Long getNextPoiLoadId() { public static Long getNextPoiLoadId() {
return Long.valueOf(POI_LOAD_IDS.getAndIncrement()); 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(); private static final AtomicLong CHUNK_RELIGHT_IDS = new AtomicLong();
public static Long getNextChunkRelightId() { public static Long getNextChunkRelightId() {
@@ -271,6 +272,16 @@ public final class ChunkTaskScheduler {
return this.lockShift; 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) { public ChunkTaskScheduler(final ServerLevel world) {
this.world = world; this.world = world;
// must be >= region shift (in paper, doesn't exist) and must be >= ticket propagator section shift // 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; 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 Long ticketId = getNextNonFullLoadId();
final int ticketLevel = getTicketLevel(status); final int ticketLevel = getTicketLevel(status);
this.chunkHolderManager.addTicketAtLevel(NON_FULL_CHUNK_LOAD, chunkX, chunkZ, ticketLevel, ticketId); 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.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread; import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil; 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.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
@@ -1270,10 +1269,10 @@ public final class NewChunkHolder {
// state upgrade // state upgrade
if (!current.isOrAfter(FullChunkStatus.FULL) && pending.isOrAfter(FullChunkStatus.FULL)) { if (!current.isOrAfter(FullChunkStatus.FULL) && pending.isOrAfter(FullChunkStatus.FULL)) {
this.updateCurrentState(FullChunkStatus.FULL); this.updateCurrentState(FullChunkStatus.FULL);
ChunkSystem.onChunkPreBorder(chunk, this.vanillaChunkHolder); PlatformHooks.get().onChunkPreBorder(chunk, this.vanillaChunkHolder);
this.scheduler.chunkHolderManager.ensureInAutosave(this); this.scheduler.chunkHolderManager.ensureInAutosave(this);
this.changeEntityChunkStatus(FullChunkStatus.FULL); this.changeEntityChunkStatus(FullChunkStatus.FULL);
ChunkSystem.onChunkBorder(chunk, this.vanillaChunkHolder); PlatformHooks.get().onChunkBorder(chunk, this.vanillaChunkHolder);
this.onFullChunkLoadChange(true, changedFullStatus); this.onFullChunkLoadChange(true, changedFullStatus);
this.completeFullStatusConsumers(FullChunkStatus.FULL, chunk); this.completeFullStatusConsumers(FullChunkStatus.FULL, chunk);
} }
@@ -1281,34 +1280,34 @@ public final class NewChunkHolder {
if (!current.isOrAfter(FullChunkStatus.BLOCK_TICKING) && pending.isOrAfter(FullChunkStatus.BLOCK_TICKING)) { if (!current.isOrAfter(FullChunkStatus.BLOCK_TICKING) && pending.isOrAfter(FullChunkStatus.BLOCK_TICKING)) {
this.updateCurrentState(FullChunkStatus.BLOCK_TICKING); this.updateCurrentState(FullChunkStatus.BLOCK_TICKING);
this.changeEntityChunkStatus(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); this.completeFullStatusConsumers(FullChunkStatus.BLOCK_TICKING, chunk);
} }
if (!current.isOrAfter(FullChunkStatus.ENTITY_TICKING) && pending.isOrAfter(FullChunkStatus.ENTITY_TICKING)) { if (!current.isOrAfter(FullChunkStatus.ENTITY_TICKING) && pending.isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
this.updateCurrentState(FullChunkStatus.ENTITY_TICKING); this.updateCurrentState(FullChunkStatus.ENTITY_TICKING);
this.changeEntityChunkStatus(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); this.completeFullStatusConsumers(FullChunkStatus.ENTITY_TICKING, chunk);
} }
} else { } else {
if (current.isOrAfter(FullChunkStatus.ENTITY_TICKING) && !pending.isOrAfter(FullChunkStatus.ENTITY_TICKING)) { if (current.isOrAfter(FullChunkStatus.ENTITY_TICKING) && !pending.isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
this.changeEntityChunkStatus(FullChunkStatus.BLOCK_TICKING); this.changeEntityChunkStatus(FullChunkStatus.BLOCK_TICKING);
ChunkSystem.onChunkNotEntityTicking(chunk, this.vanillaChunkHolder); PlatformHooks.get().onChunkNotEntityTicking(chunk, this.vanillaChunkHolder);
this.updateCurrentState(FullChunkStatus.BLOCK_TICKING); this.updateCurrentState(FullChunkStatus.BLOCK_TICKING);
} }
if (current.isOrAfter(FullChunkStatus.BLOCK_TICKING) && !pending.isOrAfter(FullChunkStatus.BLOCK_TICKING)) { if (current.isOrAfter(FullChunkStatus.BLOCK_TICKING) && !pending.isOrAfter(FullChunkStatus.BLOCK_TICKING)) {
this.changeEntityChunkStatus(FullChunkStatus.FULL); this.changeEntityChunkStatus(FullChunkStatus.FULL);
ChunkSystem.onChunkNotTicking(chunk, this.vanillaChunkHolder); PlatformHooks.get().onChunkNotTicking(chunk, this.vanillaChunkHolder);
this.updateCurrentState(FullChunkStatus.FULL); this.updateCurrentState(FullChunkStatus.FULL);
} }
if (current.isOrAfter(FullChunkStatus.FULL) && !pending.isOrAfter(FullChunkStatus.FULL)) { if (current.isOrAfter(FullChunkStatus.FULL) && !pending.isOrAfter(FullChunkStatus.FULL)) {
this.onFullChunkLoadChange(false, changedFullStatus); this.onFullChunkLoadChange(false, changedFullStatus);
this.changeEntityChunkStatus(FullChunkStatus.INACCESSIBLE); this.changeEntityChunkStatus(FullChunkStatus.INACCESSIBLE);
ChunkSystem.onChunkNotBorder(chunk, this.vanillaChunkHolder); PlatformHooks.get().onChunkNotBorder(chunk, this.vanillaChunkHolder);
ChunkSystem.onChunkPostNotBorder(chunk, this.vanillaChunkHolder); PlatformHooks.get().onChunkPostNotBorder(chunk, this.vanillaChunkHolder);
this.updateCurrentState(FullChunkStatus.INACCESSIBLE); 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)); 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; final NewChunkHolder chunkHolder = this.chunkHolder;

View File

@@ -6,4 +6,8 @@ public interface ChunkSystemTicket<T> {
public void moonrise$setRemoveDelay(final long removeDelay); 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,33 @@
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();
}

View File

@@ -3,5 +3,13 @@ package ca.spottedleaf.moonrise.patches.chunk_tick_iteration;
public final class ChunkTickConstants { public final class ChunkTickConstants {
public static final int PLAYER_SPAWN_TRACK_RANGE = 8; 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 SectionPos oldPos, final SectionPos newPos,
final boolean oldIgnore, final boolean newIgnore); 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 interface ChunkTickServerLevel {
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getPlayerTickingChunks(); public ReferenceList<LevelChunk> moonrise$getPlayerTickingChunks();
public void moonrise$markChunkForPlayerTicking(final LevelChunk chunk); public void moonrise$markChunkForPlayerTicking(final LevelChunk chunk);

View File

@@ -166,11 +166,11 @@ public final class CollisionUtil {
// startIndex and endIndex inclusive // startIndex and endIndex inclusive
// assumes indices are in range of array // assumes indices are in range of array
public static int findFloor(final double[] values, final double value, int startIndex, int endIndex) { public static int findFloor(final double[] values, final double offset, final double value, int startIndex, int endIndex) {
Objects.checkFromToIndex(startIndex, endIndex + 1, values.length); Objects.checkFromToIndex(startIndex, endIndex + 1, values.length);
do { do {
final int middle = (startIndex + endIndex) >>> 1; final int middle = (startIndex + endIndex) >>> 1;
final double middleVal = values[middle]; final double middleVal = (values[middle] + offset);
if (value < middleVal) { if (value < middleVal) {
endIndex = middle - 1; endIndex = middle - 1;
@@ -419,8 +419,6 @@ public final class CollisionUtil {
// an AABB(coords_x[x], coords_y[y], coords_z[z], coords_x[x + 1], coords_y[y + 1], coords_z[z + 1]) // an AABB(coords_x[x], coords_y[y], coords_z[z], coords_x[x + 1], coords_y[y + 1], coords_z[z + 1])
// is collidable. this is the fundamental principle of operation for the voxel collision operation // is collidable. this is the fundamental principle of operation for the voxel collision operation
// note: we should be offsetting coords, but we can also just subtract from source as well - which is
// a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that)
// note: for intersection, one we find the floor of the min we can use that as the start index // note: for intersection, one we find the floor of the min we can use that as the start index
// for the next check as source max >= source min // for the next check as source max >= source min
// note: we can fast check intersection on the two other axis by seeing if the min index is >= size, // note: we can fast check intersection on the two other axis by seeing if the min index is >= size,
@@ -429,7 +427,7 @@ public final class CollisionUtil {
final int floor_min_x = Math.max( final int floor_min_x = Math.max(
0, 0,
findFloor(coords_x, (aabb.minX - off_x) + COLLISION_EPSILON, 0, size_x) findFloor(coords_x, off_x, aabb.minX + COLLISION_EPSILON, 0, size_x)
); );
if (floor_min_x >= size_x) { if (floor_min_x >= size_x) {
// cannot intersect // cannot intersect
@@ -438,7 +436,7 @@ public final class CollisionUtil {
final int ceil_max_x = Math.min( final int ceil_max_x = Math.min(
size_x, size_x,
findFloor(coords_x, (aabb.maxX - off_x) - COLLISION_EPSILON, floor_min_x, size_x) + 1 findFloor(coords_x, off_x, aabb.maxX - COLLISION_EPSILON, floor_min_x, size_x) + 1
); );
if (floor_min_x >= ceil_max_x) { if (floor_min_x >= ceil_max_x) {
// cannot intersect // cannot intersect
@@ -447,7 +445,7 @@ public final class CollisionUtil {
final int floor_min_y = Math.max( final int floor_min_y = Math.max(
0, 0,
findFloor(coords_y, (aabb.minY - off_y) + COLLISION_EPSILON, 0, size_y) findFloor(coords_y, off_y, aabb.minY + COLLISION_EPSILON, 0, size_y)
); );
if (floor_min_y >= size_y) { if (floor_min_y >= size_y) {
// cannot intersect // cannot intersect
@@ -456,7 +454,7 @@ public final class CollisionUtil {
final int ceil_max_y = Math.min( final int ceil_max_y = Math.min(
size_y, size_y,
findFloor(coords_y, (aabb.maxY - off_y) - COLLISION_EPSILON, floor_min_y, size_y) + 1 findFloor(coords_y, off_y, aabb.maxY - COLLISION_EPSILON, floor_min_y, size_y) + 1
); );
if (floor_min_y >= ceil_max_y) { if (floor_min_y >= ceil_max_y) {
// cannot intersect // cannot intersect
@@ -465,7 +463,7 @@ public final class CollisionUtil {
final int floor_min_z = Math.max( final int floor_min_z = Math.max(
0, 0,
findFloor(coords_z, (aabb.minZ - off_z) + COLLISION_EPSILON, 0, size_z) findFloor(coords_z, off_z, aabb.minZ + COLLISION_EPSILON, 0, size_z)
); );
if (floor_min_z >= size_z) { if (floor_min_z >= size_z) {
// cannot intersect // cannot intersect
@@ -474,7 +472,7 @@ public final class CollisionUtil {
final int ceil_max_z = Math.min( final int ceil_max_z = Math.min(
size_z, size_z,
findFloor(coords_z, (aabb.maxZ - off_z) - COLLISION_EPSILON, floor_min_z, size_z) + 1 findFloor(coords_z, off_z, aabb.maxZ - COLLISION_EPSILON, floor_min_z, size_z) + 1
); );
if (floor_min_z >= ceil_max_z) { if (floor_min_z >= ceil_max_z) {
// cannot intersect // cannot intersect
@@ -530,8 +528,6 @@ public final class CollisionUtil {
// is collidable. this is the fundamental principle of operation for the voxel collision operation // is collidable. this is the fundamental principle of operation for the voxel collision operation
// note: we should be offsetting coords, but we can also just subtract from source as well - which is
// a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that)
// note: for intersection, one we find the floor of the min we can use that as the start index // note: for intersection, one we find the floor of the min we can use that as the start index
// for the next check as source max >= source min // for the next check as source max >= source min
// note: we can fast check intersection on the two other axis by seeing if the min index is >= size, // note: we can fast check intersection on the two other axis by seeing if the min index is >= size,
@@ -540,7 +536,7 @@ public final class CollisionUtil {
final int floor_min_y = Math.max( final int floor_min_y = Math.max(
0, 0,
findFloor(coords_y, (source.minY - off_y) + COLLISION_EPSILON, 0, size_y) findFloor(coords_y, off_y, source.minY + COLLISION_EPSILON, 0, size_y)
); );
if (floor_min_y >= size_y) { if (floor_min_y >= size_y) {
// cannot intersect // cannot intersect
@@ -549,7 +545,7 @@ public final class CollisionUtil {
final int ceil_max_y = Math.min( final int ceil_max_y = Math.min(
size_y, size_y,
findFloor(coords_y, (source.maxY - off_y) - COLLISION_EPSILON, floor_min_y, size_y) + 1 findFloor(coords_y, off_y, source.maxY - COLLISION_EPSILON, floor_min_y, size_y) + 1
); );
if (floor_min_y >= ceil_max_y) { if (floor_min_y >= ceil_max_y) {
// cannot intersect // cannot intersect
@@ -558,7 +554,7 @@ public final class CollisionUtil {
final int floor_min_z = Math.max( final int floor_min_z = Math.max(
0, 0,
findFloor(coords_z, (source.minZ - off_z) + COLLISION_EPSILON, 0, size_z) findFloor(coords_z, off_z, source.minZ + COLLISION_EPSILON, 0, size_z)
); );
if (floor_min_z >= size_z) { if (floor_min_z >= size_z) {
// cannot intersect // cannot intersect
@@ -567,7 +563,7 @@ public final class CollisionUtil {
final int ceil_max_z = Math.min( final int ceil_max_z = Math.min(
size_z, size_z,
findFloor(coords_z, (source.maxZ - off_z) - COLLISION_EPSILON, floor_min_z, size_z) + 1 findFloor(coords_z, off_z, source.maxZ - COLLISION_EPSILON, floor_min_z, size_z) + 1
); );
if (floor_min_z >= ceil_max_z) { if (floor_min_z >= ceil_max_z) {
// cannot intersect // cannot intersect
@@ -579,9 +575,9 @@ public final class CollisionUtil {
final long[] bitset = cached_shape_data.voxelSet(); final long[] bitset = cached_shape_data.voxelSet();
if (source_move > 0.0) { if (source_move > 0.0) {
final double source_max = source.maxX - off_x; final double source_max = source.maxX;
final int ceil_max_x = findFloor( final int ceil_max_x = findFloor(
coords_x, source_max - COLLISION_EPSILON, 0, size_x coords_x, off_x, source_max - COLLISION_EPSILON, 0, size_x
) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max ) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max
// note: only the order of the first loop matters // note: only the order of the first loop matters
@@ -590,7 +586,7 @@ public final class CollisionUtil {
final int mul_x = size_y*size_z; final int mul_x = size_y*size_z;
for (int curr_x = ceil_max_x; curr_x < size_x; ++curr_x) { for (int curr_x = ceil_max_x; curr_x < size_x; ++curr_x) {
double max_dist = coords_x[curr_x] - source_max; double max_dist = (coords_x[curr_x] + off_x) - source_max;
if (max_dist >= source_move) { if (max_dist >= source_move) {
// if we reach here, then we will never have a case where // if we reach here, then we will never have a case where
// coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1] // coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1]
@@ -617,9 +613,9 @@ public final class CollisionUtil {
return source_move; return source_move;
} else { } else {
final double source_min = source.minX - off_x; final double source_min = source.minX;
final int floor_min_x = findFloor( final int floor_min_x = findFloor(
coords_x, source_min + COLLISION_EPSILON, 0, size_x coords_x, off_x, source_min + COLLISION_EPSILON, 0, size_x
); );
// note: only the order of the first loop matters // note: only the order of the first loop matters
@@ -631,7 +627,7 @@ public final class CollisionUtil {
// thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid // thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid
final int mul_x = size_y*size_z; final int mul_x = size_y*size_z;
for (int curr_x = floor_min_x - 1; curr_x >= 0; --curr_x) { for (int curr_x = floor_min_x - 1; curr_x >= 0; --curr_x) {
double max_dist = coords_x[curr_x + 1] - source_min; double max_dist = (coords_x[curr_x + 1] + off_x) - source_min;
if (max_dist <= source_move) { if (max_dist <= source_move) {
// if we reach here, then we will never have a case where // if we reach here, then we will never have a case where
// coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1] // coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1]
@@ -688,8 +684,6 @@ public final class CollisionUtil {
// is collidable. this is the fundamental principle of operation for the voxel collision operation // is collidable. this is the fundamental principle of operation for the voxel collision operation
// note: we should be offsetting coords, but we can also just subtract from source as well - which is
// a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that)
// note: for intersection, one we find the floor of the min we can use that as the start index // note: for intersection, one we find the floor of the min we can use that as the start index
// for the next check as source max >= source min // for the next check as source max >= source min
// note: we can fast check intersection on the two other axis by seeing if the min index is >= size, // note: we can fast check intersection on the two other axis by seeing if the min index is >= size,
@@ -698,7 +692,7 @@ public final class CollisionUtil {
final int floor_min_x = Math.max( final int floor_min_x = Math.max(
0, 0,
findFloor(coords_x, (source.minX - off_x) + COLLISION_EPSILON, 0, size_x) findFloor(coords_x, off_x, source.minX + COLLISION_EPSILON, 0, size_x)
); );
if (floor_min_x >= size_x) { if (floor_min_x >= size_x) {
// cannot intersect // cannot intersect
@@ -707,7 +701,7 @@ public final class CollisionUtil {
final int ceil_max_x = Math.min( final int ceil_max_x = Math.min(
size_x, size_x,
findFloor(coords_x, (source.maxX - off_x) - COLLISION_EPSILON, floor_min_x, size_x) + 1 findFloor(coords_x, off_x, source.maxX - COLLISION_EPSILON, floor_min_x, size_x) + 1
); );
if (floor_min_x >= ceil_max_x) { if (floor_min_x >= ceil_max_x) {
// cannot intersect // cannot intersect
@@ -716,7 +710,7 @@ public final class CollisionUtil {
final int floor_min_z = Math.max( final int floor_min_z = Math.max(
0, 0,
findFloor(coords_z, (source.minZ - off_z) + COLLISION_EPSILON, 0, size_z) findFloor(coords_z, off_z, source.minZ + COLLISION_EPSILON, 0, size_z)
); );
if (floor_min_z >= size_z) { if (floor_min_z >= size_z) {
// cannot intersect // cannot intersect
@@ -725,7 +719,7 @@ public final class CollisionUtil {
final int ceil_max_z = Math.min( final int ceil_max_z = Math.min(
size_z, size_z,
findFloor(coords_z, (source.maxZ - off_z) - COLLISION_EPSILON, floor_min_z, size_z) + 1 findFloor(coords_z, off_z, source.maxZ - COLLISION_EPSILON, floor_min_z, size_z) + 1
); );
if (floor_min_z >= ceil_max_z) { if (floor_min_z >= ceil_max_z) {
// cannot intersect // cannot intersect
@@ -737,9 +731,9 @@ public final class CollisionUtil {
final long[] bitset = cached_shape_data.voxelSet(); final long[] bitset = cached_shape_data.voxelSet();
if (source_move > 0.0) { if (source_move > 0.0) {
final double source_max = source.maxY - off_y; final double source_max = source.maxY;
final int ceil_max_y = findFloor( final int ceil_max_y = findFloor(
coords_y, source_max - COLLISION_EPSILON, 0, size_y coords_y, off_y, source_max - COLLISION_EPSILON, 0, size_y
) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max ) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max
// note: only the order of the first loop matters // note: only the order of the first loop matters
@@ -748,7 +742,7 @@ public final class CollisionUtil {
final int mul_x = size_y*size_z; final int mul_x = size_y*size_z;
for (int curr_y = ceil_max_y; curr_y < size_y; ++curr_y) { for (int curr_y = ceil_max_y; curr_y < size_y; ++curr_y) {
double max_dist = coords_y[curr_y] - source_max; double max_dist = (coords_y[curr_y] + off_y) - source_max;
if (max_dist >= source_move) { if (max_dist >= source_move) {
// if we reach here, then we will never have a case where // if we reach here, then we will never have a case where
// coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1] // coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1]
@@ -775,9 +769,9 @@ public final class CollisionUtil {
return source_move; return source_move;
} else { } else {
final double source_min = source.minY - off_y; final double source_min = source.minY;
final int floor_min_y = findFloor( final int floor_min_y = findFloor(
coords_y, source_min + COLLISION_EPSILON, 0, size_y coords_y, off_y, source_min + COLLISION_EPSILON, 0, size_y
); );
// note: only the order of the first loop matters // note: only the order of the first loop matters
@@ -789,7 +783,7 @@ public final class CollisionUtil {
// thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid // thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid
final int mul_x = size_y*size_z; final int mul_x = size_y*size_z;
for (int curr_y = floor_min_y - 1; curr_y >= 0; --curr_y) { for (int curr_y = floor_min_y - 1; curr_y >= 0; --curr_y) {
double max_dist = coords_y[curr_y + 1] - source_min; double max_dist = (coords_y[curr_y + 1] + off_y) - source_min;
if (max_dist <= source_move) { if (max_dist <= source_move) {
// if we reach here, then we will never have a case where // if we reach here, then we will never have a case where
// coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1] // coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1]
@@ -846,8 +840,6 @@ public final class CollisionUtil {
// is collidable. this is the fundamental principle of operation for the voxel collision operation // is collidable. this is the fundamental principle of operation for the voxel collision operation
// note: we should be offsetting coords, but we can also just subtract from source as well - which is
// a win in terms of ops / simplicity (see findFloor, allows us to not modify coords for that)
// note: for intersection, one we find the floor of the min we can use that as the start index // note: for intersection, one we find the floor of the min we can use that as the start index
// for the next check as source max >= source min // for the next check as source max >= source min
// note: we can fast check intersection on the two other axis by seeing if the min index is >= size, // note: we can fast check intersection on the two other axis by seeing if the min index is >= size,
@@ -856,7 +848,7 @@ public final class CollisionUtil {
final int floor_min_x = Math.max( final int floor_min_x = Math.max(
0, 0,
findFloor(coords_x, (source.minX - off_x) + COLLISION_EPSILON, 0, size_x) findFloor(coords_x, off_x, source.minX + COLLISION_EPSILON, 0, size_x)
); );
if (floor_min_x >= size_x) { if (floor_min_x >= size_x) {
// cannot intersect // cannot intersect
@@ -865,7 +857,7 @@ public final class CollisionUtil {
final int ceil_max_x = Math.min( final int ceil_max_x = Math.min(
size_x, size_x,
findFloor(coords_x, (source.maxX - off_x) - COLLISION_EPSILON, floor_min_x, size_x) + 1 findFloor(coords_x, off_x, source.maxX - COLLISION_EPSILON, floor_min_x, size_x) + 1
); );
if (floor_min_x >= ceil_max_x) { if (floor_min_x >= ceil_max_x) {
// cannot intersect // cannot intersect
@@ -874,7 +866,7 @@ public final class CollisionUtil {
final int floor_min_y = Math.max( final int floor_min_y = Math.max(
0, 0,
findFloor(coords_y, (source.minY - off_y) + COLLISION_EPSILON, 0, size_y) findFloor(coords_y, off_y, source.minY + COLLISION_EPSILON, 0, size_y)
); );
if (floor_min_y >= size_y) { if (floor_min_y >= size_y) {
// cannot intersect // cannot intersect
@@ -883,7 +875,7 @@ public final class CollisionUtil {
final int ceil_max_y = Math.min( final int ceil_max_y = Math.min(
size_y, size_y,
findFloor(coords_y, (source.maxY - off_y) - COLLISION_EPSILON, floor_min_y, size_y) + 1 findFloor(coords_y, off_y, source.maxY - COLLISION_EPSILON, floor_min_y, size_y) + 1
); );
if (floor_min_y >= ceil_max_y) { if (floor_min_y >= ceil_max_y) {
// cannot intersect // cannot intersect
@@ -895,9 +887,9 @@ public final class CollisionUtil {
final long[] bitset = cached_shape_data.voxelSet(); final long[] bitset = cached_shape_data.voxelSet();
if (source_move > 0.0) { if (source_move > 0.0) {
final double source_max = source.maxZ - off_z; final double source_max = source.maxZ;
final int ceil_max_z = findFloor( final int ceil_max_z = findFloor(
coords_z, source_max - COLLISION_EPSILON, 0, size_z coords_z, off_z, source_max - COLLISION_EPSILON, 0, size_z
) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max ) + 1; // add one, we are not interested in (coords[i] + COLLISION_EPSILON) < max
// note: only the order of the first loop matters // note: only the order of the first loop matters
@@ -906,7 +898,7 @@ public final class CollisionUtil {
final int mul_x = size_y*size_z; final int mul_x = size_y*size_z;
for (int curr_z = ceil_max_z; curr_z < size_z; ++curr_z) { for (int curr_z = ceil_max_z; curr_z < size_z; ++curr_z) {
double max_dist = coords_z[curr_z] - source_max; double max_dist = (coords_z[curr_z] + off_z) - source_max;
if (max_dist >= source_move) { if (max_dist >= source_move) {
// if we reach here, then we will never have a case where // if we reach here, then we will never have a case where
// coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1] // coords[curr + n] - source_max < source_move, as coords[curr + n] < coords[curr + n + 1]
@@ -933,9 +925,9 @@ public final class CollisionUtil {
return source_move; return source_move;
} else { } else {
final double source_min = source.minZ - off_z; final double source_min = source.minZ;
final int floor_min_z = findFloor( final int floor_min_z = findFloor(
coords_z, source_min + COLLISION_EPSILON, 0, size_z coords_z, off_z, source_min + COLLISION_EPSILON, 0, size_z
); );
// note: only the order of the first loop matters // note: only the order of the first loop matters
@@ -947,7 +939,7 @@ public final class CollisionUtil {
// thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid // thus, we need to use the voxel index i-1 if we want to check that the face at index i is solid
final int mul_x = size_y*size_z; final int mul_x = size_y*size_z;
for (int curr_z = floor_min_z - 1; curr_z >= 0; --curr_z) { for (int curr_z = floor_min_z - 1; curr_z >= 0; --curr_z) {
double max_dist = coords_z[curr_z + 1] - source_min; double max_dist = (coords_z[curr_z + 1] + off_z) - source_min;
if (max_dist <= source_move) { if (max_dist <= source_move) {
// if we reach here, then we will never have a case where // if we reach here, then we will never have a case where
// coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1] // coords[curr + n] - source_max > source_move, as coords[curr + n] > coords[curr + n - 1]
@@ -982,7 +974,7 @@ public final class CollisionUtil {
} }
// does not use epsilon // does not use epsilon
public static boolean strictlyContains(final VoxelShape voxel, double x, double y, double z) { public static boolean strictlyContains(final VoxelShape voxel, final double x, final double y, final double z) {
final AABB single_aabb = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation(); final AABB single_aabb = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
if (single_aabb != null) { if (single_aabb != null) {
return single_aabb.contains(x, y, z); return single_aabb.contains(x, y, z);
@@ -993,10 +985,9 @@ public final class CollisionUtil {
return false; return false;
} }
// offset input final double off_x = ((CollisionVoxelShape)voxel).moonrise$offsetX();
x -= ((CollisionVoxelShape)voxel).moonrise$offsetX(); final double off_y = ((CollisionVoxelShape)voxel).moonrise$offsetY();
y -= ((CollisionVoxelShape)voxel).moonrise$offsetY(); final double off_z = ((CollisionVoxelShape)voxel).moonrise$offsetZ();
z -= ((CollisionVoxelShape)voxel).moonrise$offsetZ();
final double[] coords_x = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesX(); final double[] coords_x = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesX();
final double[] coords_y = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesY(); final double[] coords_y = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesY();
@@ -1012,17 +1003,17 @@ public final class CollisionUtil {
// note: should mirror AABB#contains, which is that for any point X that X >= min and X < max. // note: should mirror AABB#contains, which is that for any point X that X >= min and X < max.
// specifically, it cannot collide on the max bounds of the shape // specifically, it cannot collide on the max bounds of the shape
final int index_x = findFloor(coords_x, x, 0, size_x); final int index_x = findFloor(coords_x, off_x, x, 0, size_x);
if (index_x < 0 || index_x >= size_x) { if (index_x < 0 || index_x >= size_x) {
return false; return false;
} }
final int index_y = findFloor(coords_y, y, 0, size_y); final int index_y = findFloor(coords_y, off_y, y, 0, size_y);
if (index_y < 0 || index_y >= size_y) { if (index_y < 0 || index_y >= size_y) {
return false; return false;
} }
final int index_z = findFloor(coords_z, z, 0, size_z); final int index_z = findFloor(coords_z, off_z, z, 0, size_z);
if (index_z < 0 || index_z >= size_z) { if (index_z < 0 || index_z >= size_z) {
return false; return false;
} }
@@ -1695,74 +1686,56 @@ public final class CollisionUtil {
public static double performAABBCollisionsX(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) { public static double performAABBCollisionsX(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) {
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
if (Math.abs(value) < COLLISION_EPSILON) {
return 0.0;
}
final AABB target = potentialCollisions.get(i); final AABB target = potentialCollisions.get(i);
value = collideX(target, currentBoundingBox, value); value = collideX(target, currentBoundingBox, value);
} }
return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value; return value;
} }
public static double performAABBCollisionsY(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) { public static double performAABBCollisionsY(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) {
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
if (Math.abs(value) < COLLISION_EPSILON) {
return 0.0;
}
final AABB target = potentialCollisions.get(i); final AABB target = potentialCollisions.get(i);
value = collideY(target, currentBoundingBox, value); value = collideY(target, currentBoundingBox, value);
} }
return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value; return value;
} }
public static double performAABBCollisionsZ(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) { public static double performAABBCollisionsZ(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) {
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
if (Math.abs(value) < COLLISION_EPSILON) {
return 0.0;
}
final AABB target = potentialCollisions.get(i); final AABB target = potentialCollisions.get(i);
value = collideZ(target, currentBoundingBox, value); value = collideZ(target, currentBoundingBox, value);
} }
return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value; return value;
} }
public static double performVoxelCollisionsX(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) { public static double performVoxelCollisionsX(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) {
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
if (Math.abs(value) < COLLISION_EPSILON) {
return 0.0;
}
final VoxelShape target = potentialCollisions.get(i); final VoxelShape target = potentialCollisions.get(i);
value = collideX(target, currentBoundingBox, value); value = collideX(target, currentBoundingBox, value);
} }
return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value; return value;
} }
public static double performVoxelCollisionsY(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) { public static double performVoxelCollisionsY(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) {
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
if (Math.abs(value) < COLLISION_EPSILON) {
return 0.0;
}
final VoxelShape target = potentialCollisions.get(i); final VoxelShape target = potentialCollisions.get(i);
value = collideY(target, currentBoundingBox, value); value = collideY(target, currentBoundingBox, value);
} }
return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value; return value;
} }
public static double performVoxelCollisionsZ(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) { public static double performVoxelCollisionsZ(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) {
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) { for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
if (Math.abs(value) < COLLISION_EPSILON) {
return 0.0;
}
final VoxelShape target = potentialCollisions.get(i); final VoxelShape target = potentialCollisions.get(i);
value = collideZ(target, currentBoundingBox, value); value = collideZ(target, currentBoundingBox, value);
} }
return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value; return value;
} }
public static Vec3 performVoxelCollisions(final Vec3 moveVector, AABB axisalignedbb, final List<VoxelShape> potentialCollisions) { public static Vec3 performVoxelCollisions(final Vec3 moveVector, AABB axisalignedbb, final List<VoxelShape> potentialCollisions) {
@@ -2031,11 +2004,10 @@ public final class CollisionUtil {
VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape(); VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();
if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) { if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
mutablePos.set(blockX, blockY, blockZ);
if (useEntityCollisionShape) { if (useEntityCollisionShape) {
mutablePos.set(blockX, blockY, blockZ);
blockCollision = collisionShape.getCollisionShape(blockData, world, mutablePos); blockCollision = collisionShape.getCollisionShape(blockData, world, mutablePos);
} else if (blockCollision == null) { } else if (blockCollision == null) {
mutablePos.set(blockX, blockY, blockZ);
blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape);
} }
@@ -2047,7 +2019,6 @@ public final class CollisionUtil {
} }
if (predicate != null) { if (predicate != null) {
mutablePos.set(blockX, blockY, blockZ);
if (!predicate.test(blockData, mutablePos)) { if (!predicate.test(blockData, mutablePos)) {
continue; continue;
} }
@@ -2073,7 +2044,6 @@ public final class CollisionUtil {
} }
if (predicate != null) { if (predicate != null) {
mutablePos.set(blockX, blockY, blockZ);
if (!predicate.test(blockData, mutablePos)) { if (!predicate.test(blockData, mutablePos)) {
continue; continue;
} }
@@ -2153,7 +2123,7 @@ public final class CollisionUtil {
private boolean delegated; private boolean delegated;
public LazyEntityCollisionContext(final Entity entity) { public LazyEntityCollisionContext(final Entity entity) {
super(false, 0.0, null, null, entity); super(false, false, 0.0, null, null, entity);
} }
public static boolean useEntityCollisionShape(final Level world, final Entity entity) { public static boolean useEntityCollisionShape(final Level world, final Entity entity) {
@@ -2183,6 +2153,11 @@ public final class CollisionUtil {
return this.getDelegate().isDescending(); return this.getDelegate().isDescending();
} }
@Override
public boolean isPlacement() {
return this.getDelegate().isPlacement();
}
@Override @Override
public boolean isAbove(final VoxelShape shape, final BlockPos pos, final boolean defaultValue) { public boolean isAbove(final VoxelShape shape, final BlockPos pos, final boolean defaultValue) {
return this.getDelegate().isAbove(shape, pos, defaultValue); return this.getDelegate().isAbove(shape, pos, defaultValue);

View File

@@ -182,7 +182,7 @@ public abstract class StarLightEngine {
for (int dx = -radius; dx <= radius; ++dx) { for (int dx = -radius; dx <= radius; ++dx) {
final int cx = centerChunkX + dx; final int cx = centerChunkX + dx;
final int cz = centerChunkZ + dz; final int cz = centerChunkZ + dz;
final boolean isTwoRadius = Math.max(IntegerUtil.branchlessAbs(dx), IntegerUtil.branchlessAbs(dz)) == 2; final boolean isTwoRadius = Math.max(Math.abs(dx), Math.abs(dz)) == 2;
final ChunkAccess chunk = (ChunkAccess)chunkProvider.getChunkForLighting(cx, cz); final ChunkAccess chunk = (ChunkAccess)chunkProvider.getChunkForLighting(cx, cz);
if (chunk == null) { if (chunk == null) {

View File

@@ -9,6 +9,7 @@ import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus; import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk; import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.shorts.ShortCollection; import it.unimi.dsi.fastutil.shorts.ShortCollection;
@@ -39,10 +40,8 @@ import java.util.function.IntConsumer;
public final class StarLightInterface { public final class StarLightInterface {
public static final TicketType<Long> CHUNK_WORK_TICKET = TicketType.create("starlight:chunk_work_ticket", Long::compareTo); public static final TicketType CHUNK_WORK_TICKET = ChunkSystemTicketType.create("starlight:chunk_work_ticket", Long::compareTo);
public static final int LIGHT_TICKET_LEVEL = ChunkLevel.byStatus(ChunkStatus.LIGHT); public static final int LIGHT_TICKET_LEVEL = ChunkLevel.byStatus(ChunkStatus.LIGHT);
// ticket level = ChunkLevel.byStatus(FullChunkStatus.FULL) - input
public static final int REGION_LIGHT_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.FULL) - LIGHT_TICKET_LEVEL;
/** /**
* Can be {@code null}, indicating the light is all empty. * Can be {@code null}, indicating the light is all empty.

View File

@@ -56,15 +56,18 @@ public final class SaveUtil {
tag.putBoolean("isLightOn", false); tag.putBoolean("isLightOn", false);
} }
// diff end - store our tag for whether light data is init'd // diff end - store our tag for whether light data is init'd
ChunkStatus status = ChunkStatus.byName(tag.getString("Status")); ChunkStatus status = tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY);
CompoundTag[] sections = new CompoundTag[maxSection - minSection + 1]; CompoundTag[] sections = new CompoundTag[maxSection - minSection + 1];
ListTag sectionsStored = tag.getList("sections", 10); ListTag sectionsStored = tag.getListOrEmpty("sections");
for (int i = 0; i < sectionsStored.size(); ++i) { for (int i = 0; i < sectionsStored.size(); ++i) {
CompoundTag sectionStored = sectionsStored.getCompound(i); CompoundTag sectionStored = sectionsStored.getCompound(i).orElse(null);
int k = sectionStored.getByte("Y"); if (sectionStored == null) {
continue;
}
int k = sectionStored.getByteOr("Y", (byte)0);
// strip light data // strip light data
sectionStored.remove("BlockLight"); sectionStored.remove("BlockLight");
@@ -147,33 +150,38 @@ public final class SaveUtil {
// start copy from the original method // start copy from the original method
boolean lit = tag.get("isLightOn") != null && tag.getInt(STARLIGHT_VERSION_TAG) == STARLIGHT_LIGHT_VERSION; boolean lit = tag.get("isLightOn") != null && tag.getIntOr(STARLIGHT_VERSION_TAG, -1) == STARLIGHT_LIGHT_VERSION;
boolean canReadSky = world.dimensionType().hasSkyLight(); boolean canReadSky = world.dimensionType().hasSkyLight();
ChunkStatus status = ChunkStatus.byName(tag.getString("Status")); ChunkStatus status = tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY);
if (lit && status.isOrAfter(ChunkStatus.LIGHT)) { // diff - we add the status check here if (lit && status.isOrAfter(ChunkStatus.LIGHT)) { // diff - we add the status check here
ListTag sections = tag.getList("sections", 10); ListTag sections = tag.getListOrEmpty("sections");
for (int i = 0; i < sections.size(); ++i) { for (int i = 0; i < sections.size(); ++i) {
CompoundTag sectionData = sections.getCompound(i); CompoundTag sectionData = sections.getCompound(i).orElse(null);
int y = sectionData.getByte("Y"); if (sectionData == null) {
continue;
}
int y = sectionData.getByteOr("Y", (byte)0);
if (sectionData.contains("BlockLight", 7)) { final byte[] blockLight = sectionData.getByteArray("BlockLight").orElse(null);
if (blockLight != null) {
// this is where our diff is // this is where our diff is
blockNibbles[y - minSection] = new SWMRNibbleArray(sectionData.getByteArray("BlockLight").clone(), sectionData.getInt(BLOCKLIGHT_STATE_TAG)); // clone for data safety blockNibbles[y - minSection] = new SWMRNibbleArray(blockLight.clone(), sectionData.getIntOr(BLOCKLIGHT_STATE_TAG, 0)); // clone for data safety
} else { } else {
blockNibbles[y - minSection] = new SWMRNibbleArray(null, sectionData.getInt(BLOCKLIGHT_STATE_TAG)); blockNibbles[y - minSection] = new SWMRNibbleArray(null, sectionData.getIntOr(BLOCKLIGHT_STATE_TAG, 0));
} }
if (canReadSky) { if (canReadSky) {
if (sectionData.contains("SkyLight", 7)) { final byte[] skyLight = sectionData.getByteArray("SkyLight").orElse(null);
if (skyLight != null) {
// we store under the same key so mod programs editing nbt // we store under the same key so mod programs editing nbt
// can still read the data, hopefully. // can still read the data, hopefully.
// however, for compatibility we store chunks as unlit so vanilla // however, for compatibility we store chunks as unlit so vanilla
// is forced to re-light them if it encounters our data. It's too much of a burden // is forced to re-light them if it encounters our data. It's too much of a burden
// to try and maintain compatibility with a broken and inferior skylight management system. // to try and maintain compatibility with a broken and inferior skylight management system.
skyNibbles[y - minSection] = new SWMRNibbleArray(sectionData.getByteArray("SkyLight").clone(), sectionData.getInt(SKYLIGHT_STATE_TAG)); // clone for data safety skyNibbles[y - minSection] = new SWMRNibbleArray(skyLight.clone(), sectionData.getIntOr(SKYLIGHT_STATE_TAG, 0)); // clone for data safety
} else { } else {
skyNibbles[y - minSection] = new SWMRNibbleArray(null, sectionData.getInt(SKYLIGHT_STATE_TAG)); skyNibbles[y - minSection] = new SWMRNibbleArray(null, sectionData.getIntOr(SKYLIGHT_STATE_TAG, 0));
} }
} }
} }

View File

@@ -22,6 +22,8 @@ accessible field net/minecraft/world/level/chunk/PalettedContainer data Lnet/min
# PalettedContainer.Data # PalettedContainer.Data
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
# MDG requires we AT the constructor if we AT the class
accessible method net/minecraft/world/level/chunk/PalettedContainer$Data <init> (Lnet/minecraft/world/level/chunk/PalettedContainer$Configuration;Lnet/minecraft/util/BitStorage;Lnet/minecraft/world/level/chunk/Palette;)V
# PaletteResize # PaletteResize
@@ -150,6 +152,11 @@ accessible method net/minecraft/world/level/material/Fluid isEmpty ()Z
accessible method net/minecraft/world/level/material/Fluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState; accessible method net/minecraft/world/level/material/Fluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState;
accessible method net/minecraft/world/level/material/Fluid isRandomlyTicking ()Z accessible method net/minecraft/world/level/material/Fluid isRandomlyTicking ()Z
# We need to manually copy these down for MDG
accessible method net/minecraft/world/level/material/EmptyFluid isEmpty ()Z
accessible method net/minecraft/world/level/material/EmptyFluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState;
accessible method net/minecraft/world/level/material/LavaFluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState;
accessible method net/minecraft/world/level/material/LavaFluid isRandomlyTicking ()Z
# VisibilitySet # VisibilitySet
accessible field net/minecraft/client/renderer/chunk/VisibilitySet FACINGS I accessible field net/minecraft/client/renderer/chunk/VisibilitySet FACINGS I
@@ -187,9 +194,8 @@ accessible class net/minecraft/server/level/ChunkMap$DistanceManager
# DistanceManager # DistanceManager
mutable field net/minecraft/server/level/DistanceManager playersPerChunk Lit/unimi/dsi/fastutil/longs/Long2ObjectMap; mutable field net/minecraft/server/level/DistanceManager playersPerChunk Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;
mutable field net/minecraft/server/level/DistanceManager tickets Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap; mutable field net/minecraft/server/level/DistanceManager loadingChunkTracker Lnet/minecraft/server/level/LoadingChunkTracker;
mutable field net/minecraft/server/level/DistanceManager ticketTracker Lnet/minecraft/server/level/DistanceManager$ChunkTicketTracker; mutable field net/minecraft/server/level/DistanceManager simulationChunkTracker Lnet/minecraft/server/level/SimulationChunkTracker;
mutable field net/minecraft/server/level/DistanceManager tickingTicketsTracker Lnet/minecraft/server/level/TickingTracker;
mutable field net/minecraft/server/level/DistanceManager playerTicketManager Lnet/minecraft/server/level/DistanceManager$PlayerTicketTracker; mutable field net/minecraft/server/level/DistanceManager playerTicketManager Lnet/minecraft/server/level/DistanceManager$PlayerTicketTracker;
mutable field net/minecraft/server/level/DistanceManager chunksToUpdateFutures Ljava/util/Set; mutable field net/minecraft/server/level/DistanceManager chunksToUpdateFutures Ljava/util/Set;
mutable field net/minecraft/server/level/DistanceManager ticketDispatcher Lnet/minecraft/server/level/ThrottlingChunkTaskDispatcher; mutable field net/minecraft/server/level/DistanceManager ticketDispatcher Lnet/minecraft/server/level/ThrottlingChunkTaskDispatcher;
@@ -198,10 +204,6 @@ mutable field net/minecraft/server/level/DistanceManager mainThreadExecutor Ljav
mutable field net/minecraft/server/level/DistanceManager naturalSpawnChunkCounter Lnet/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker; mutable field net/minecraft/server/level/DistanceManager naturalSpawnChunkCounter Lnet/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker;
# DistanceManager$ChunkTicketTracker
accessible class net/minecraft/server/level/DistanceManager$ChunkTicketTracker
# DistanceManager$PlayerTicketTracker # DistanceManager$PlayerTicketTracker
accessible class net/minecraft/server/level/DistanceManager$PlayerTicketTracker accessible class net/minecraft/server/level/DistanceManager$PlayerTicketTracker
@@ -210,11 +212,6 @@ accessible class net/minecraft/server/level/DistanceManager$PlayerTicketTracker
accessible class net/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker accessible class net/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker
# Ticket
accessible field net/minecraft/server/level/Ticket key Ljava/lang/Object;
accessible field net/minecraft/server/level/TicketType timeout J
# ServerChunkCache # ServerChunkCache
accessible method net/minecraft/server/level/ServerChunkCache runDistanceManagerUpdates ()Z accessible method net/minecraft/server/level/ServerChunkCache runDistanceManagerUpdates ()Z
accessible field net/minecraft/server/level/ServerChunkCache level Lnet/minecraft/server/level/ServerLevel; accessible field net/minecraft/server/level/ServerChunkCache level Lnet/minecraft/server/level/ServerLevel;
@@ -234,14 +231,6 @@ accessible class net/minecraft/server/level/ServerLevel$EntityCallbacks
accessible method net/minecraft/server/level/ServerLevel$EntityCallbacks <init> (Lnet/minecraft/server/level/ServerLevel;)V accessible method net/minecraft/server/level/ServerLevel$EntityCallbacks <init> (Lnet/minecraft/server/level/ServerLevel;)V
# EntityStorage
accessible method net/minecraft/world/level/chunk/storage/EntityStorage readChunkPos (Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/world/level/ChunkPos;
accessible method net/minecraft/world/level/chunk/storage/EntityStorage writeChunkPos (Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/level/ChunkPos;)V
# Ticket
accessible method net/minecraft/server/level/Ticket <init> (Lnet/minecraft/server/level/TicketType;ILjava/lang/Object;)V
# ChunkStorage # ChunkStorage
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker; accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
mutable field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker; mutable field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -278,11 +267,6 @@ accessible class net/minecraft/server/level/ChunkMap$TrackedEntity
accessible field net/minecraft/server/level/ChunkMap$TrackedEntity serverEntity Lnet/minecraft/server/level/ServerEntity; accessible field net/minecraft/server/level/ChunkMap$TrackedEntity serverEntity Lnet/minecraft/server/level/ServerEntity;
# ServerChunkCache$ChunkAndHolder
accessible class net/minecraft/server/level/ServerChunkCache$ChunkAndHolder
accessible method net/minecraft/server/level/ServerChunkCache$ChunkAndHolder <init> (Lnet/minecraft/world/level/chunk/LevelChunk;Lnet/minecraft/server/level/ChunkHolder;)V
# LevelLoadStatusManager$Status # LevelLoadStatusManager$Status
accessible class net/minecraft/client/multiplayer/LevelLoadStatusManager$Status accessible class net/minecraft/client/multiplayer/LevelLoadStatusManager$Status
@@ -305,3 +289,17 @@ accessible class net/minecraft/world/level/LocalMobCapCalculator$MobCounts
# SectionStorage$PackedChunk # SectionStorage$PackedChunk
accessible class net/minecraft/world/level/chunk/storage/SectionStorage$PackedChunk accessible class net/minecraft/world/level/chunk/storage/SectionStorage$PackedChunk
# MDG requires we AT the constructor if we AT the class
accessible method net/minecraft/world/level/chunk/storage/SectionStorage$PackedChunk <init> (Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;Z)V
# Ticket
accessible method net/minecraft/server/level/Ticket <init> (Lnet/minecraft/server/level/TicketType;IJ)V
# LoadingChunkTracker
accessible class net/minecraft/server/level/LoadingChunkTracker
# TicketStorage
mutable field net/minecraft/world/level/TicketStorage tickets Lit/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap;

View File

@@ -26,6 +26,7 @@
"chunk_system.ChunkStepMixin", "chunk_system.ChunkStepMixin",
"chunk_system.ChunkStorageMixin", "chunk_system.ChunkStorageMixin",
"chunk_system.DistanceManagerMixin", "chunk_system.DistanceManagerMixin",
"chunk_system.DynamicGameEventListenerMixin",
"chunk_system.EntityGetterMixin", "chunk_system.EntityGetterMixin",
"chunk_system.EntityMixin", "chunk_system.EntityMixin",
"chunk_system.EntityTickListMixin", "chunk_system.EntityTickListMixin",
@@ -51,6 +52,8 @@
"chunk_system.StructureCheckMixin", "chunk_system.StructureCheckMixin",
"chunk_system.StructureTemplate$PaletteMixin", "chunk_system.StructureTemplate$PaletteMixin",
"chunk_system.TicketMixin", "chunk_system.TicketMixin",
"chunk_system.TicketStorageMixin",
"chunk_system.TicketTypeMixin",
"chunk_tick_iteration.ChunkMapMixin", "chunk_tick_iteration.ChunkMapMixin",
"chunk_tick_iteration.DistanceManagerMixin", "chunk_tick_iteration.DistanceManagerMixin",
"chunk_tick_iteration.ServerChunkCacheMixin", "chunk_tick_iteration.ServerChunkCacheMixin",