Compare commits

..

73 Commits

Author SHA1 Message Date
Jason Penilla
81927092f1 Bump resource-factory 2025-05-14 09:08:26 -07:00
Jason Penilla
0f0da2998a Generate mod metadata files with resource factory instead of using processResources 2025-05-10 15:34:00 -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
Spottedleaf
a6cf977c5f Set version to 0.2.0-beta.5 2024-12-01 16:00:29 -08:00
Spottedleaf
8ed23bc8f3 Fix several off-by-one errors in view distance calculations
1. For NearbyPlayers, we need to be using the view distance, and
   not the load distance (which is +1 of the view distance).
2. Correctly clamp tick distance to view distance. Since
   load distance is +1 of view distance, we need to subtract
   one from the load distance when clamping.

Additionally, add checks inside ViewDistances to ensure that
the inputs are in range to catch future errors.
2024-12-01 15:30:33 -08:00
Spottedleaf
7034995878 Clamp simulation distance
Values above MAX_VIEW_DISTANCE do not make sense to configure,
as it is clamped to the load view distance.
2024-12-01 15:29:49 -08:00
Spottedleaf
ca3d776562 Adjust constant collision shape determination
Our previous hack did not actually catch every case. For now,
it will only assume a constant collision shape of EMPTY for
air blocks.

Fixes https://github.com/PaperMC/Paper/issues/11697
2024-12-01 12:00:31 -08:00
Spottedleaf
1a077389c2 Force LazyEntityCollisionContext#getEntity() to delegate
By delegating when the entity is retrieved, we can correctly catch
cases where the collision method is inspecting some entity state.
2024-12-01 12:00:18 -08:00
Spottedleaf
49bcc60cf5 Only call config initialisers if reload succeeded 2024-12-01 11:31:16 -08:00
Spottedleaf
8b1f31ade8 Drop RegionFileStorage.RegionFileSizeException diff in MoonriseRegionFileIO
This diff should be implemented in Paper. Previously, the diff was
in the chunk system patch in Paper - but it should be in its own
separate patch.
2024-11-28 17:53:36 -08:00
Spottedleaf
4d88e04e3c Do not call modifyEntityTrackingRange on own range
The range is already modified, so this call will not do anything.
2024-11-27 12:20:50 -08:00
Spottedleaf
33889c3850 Replace Level/Entity randoms with ThreadUnsafeRandom
This avoids the expensive CAS logic at the expense of losing the
thread check.
2024-11-27 05:56:34 -08:00
Spottedleaf
37ac3003cb Replace SimpleRandom with (Simple)ThreadUnsafeRandom
ThreadUnsafeRandom is a random implementation that is
identical to LegacyRandomSource behaviourally, but
without the thread checks.

SimpleThreadUnsafeRandom is ThreadUnsafeRandom except with
its nextInt(int) function replaced with a faster
but more biased implementation when bound is very large.
2024-11-27 05:56:34 -08:00
Jason Penilla
28128d06c1 Disable immediate loading screen closure by default 2024-11-26 15:51:35 -07:00
Spottedleaf
6f9620787b Use https for YamlConfig repo
SSH isn't setup in actions so it cannot clone via SSH.
2024-11-25 09:30:49 -08:00
Spottedleaf
cbf6c118dc Mark install_deps.sh as executable 2024-11-25 09:25:32 -08:00
Spottedleaf
d44fa1f8aa Move YamlConfig to own project 2024-11-25 09:17:11 -08:00
Spottedleaf
3d9ae3f018 Fix non block ticking chunks not sending block/light updates
Needed to redirect the getTickingChunk call in
broadcastChangedChunks to use the chunk to send method.
2024-11-18 13:24:24 -08:00
Spottedleaf
d24f6c2874 Do not create unneccessary callback in ChunkTaskScheduler#scheduleChunkLoad
If the parameter has addTicket = false and onComplete = null,
then the loadCallback would do no work and as a result does
not need to be created.
2024-11-17 14:09:00 -08:00
Jason Penilla
f190cdd8cb Back to 0.2.0-SNAPSHOT 2024-11-17 11:31:37 -07:00
Jason Penilla
e7510eda16 0.2.0-beta.4 2024-11-17 10:49:05 -07:00
Jason Penilla
d9442c1492 Update lithium overrides 2024-11-17 10:28:38 -07:00
Jason Penilla
93eb2786f2 Update ServerExplosionMixin for NeoForge changes 2024-11-17 10:27:31 -07:00
Jason Penilla
1bef6823c5 Update lithium, NeoForge, loom 2024-11-16 17:35:27 -07:00
Jason Penilla
01152eec95 Update NeoForge lithium overrides 2024-11-16 17:34:44 -07:00
Spottedleaf
1e39f5370a Fix experimental minecart collisions on sloped rails
We are supposed to ignore some collisions on the sloped
rail.
2024-11-14 13:31:22 -08:00
Jason Penilla
ea50ba38ea Apply FerriteCore config overrides automatically
New versions of FC added this mechanism

closes #66
2024-11-05 16:12:37 -07:00
Spottedleaf
c00b9fcd7b Move back to 0.2.0-SNAPSHOT 2024-11-02 16:54:15 -07:00
136 changed files with 2882 additions and 3036 deletions

View File

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

3
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

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

Submodule ConcurrentUtil deleted from 08d3ca3241

View File

@@ -24,7 +24,7 @@ patches. Listed below are notable patches:
| Mod | Status |
|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Lithium | <details><summary>✅ compatible</summary>Lithium optimises many of the same parts of the game as Moonrise, for example the chunk system. Moonrise will automatically disable conflicting parts of Lithium. This mechanism needs to be manually validated for each Moonrise and Lithium release.</details> |
| FerriteCore | <details><summary>📝 requires config changes</summary>In `config/ferritecore-mixin.toml`:<br/>Set `replaceNeighborLookup` and `replacePropertyMap` to `false`</details> |
| FerriteCore | <details><summary>✅ compatible</summary>FerriteCore optimises some of the same parts of the game as Moonrise. Moonrise will automatically disable conflicting parts of FerriteCore. This mechanism needs to be manually validated for each Moonrise and FerriteCore release.</details> |
| C2ME | <details><summary>❌ incompatible</summary>C2ME is based around modifications to the chunk system, which Moonrise replaces wholesale. This makes them fundamentally incompatible.</details> |
## Configuration

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,52 +1,115 @@
plugins {
id("xyz.jpenilla.quiet-architectury-loom")
id 'maven-publish'
id 'com.gradleup.shadow'
}
import java.util.stream.Collectors
import net.fabricmc.loom.util.gradle.SourceSetHelper
import xyz.jpenilla.resourcefactory.fabric.Environment
configurations.create("libs")
configurations.shadow {
extendsFrom(configurations.libs)
}
configurations.implementation {
extendsFrom(configurations.libs)
plugins {
id("quiet-fabric-loom")
id 'maven-publish'
id 'xyz.jpenilla.resource-factory-fabric-convention'
}
dependencies {
add('shadow', project([path: ":", configuration: "namedElements"]))
runtimeOnly(project(":").sourceSets.main.output)
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
runtimeOnly(project(":").sourceSets.main.output)
shadow(project(":"))
compileOnly(project(":"))
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
modImplementation platform(fabricApiLibs.bom)
modImplementation fabricApiLibs.command.api.v2
modImplementation fabricApiLibs.lifecycle.events.v1
include fabricApiLibs.command.api.v2
include fabricApiLibs.base
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
}
}
shadowJar {
tasks.shadowJar {
archiveClassifier = "dev-all"
destinationDirectory = layout.buildDirectory.dir("libs")
configurations = [project.configurations.shadow]
relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil'
relocate 'ca.spottedleaf.yamlconfig', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.yamlconfig'
relocate 'org.yaml.snakeyaml', 'ca.spottedleaf.moonrise.libs.org.yaml.snakeyaml'
}
fabricModJson {
id = "moonrise"
name = "Moonrise"
description = "Optimisation mod for the dedicated and integrated server."
author("Spottedleaf")
contact {
issues = "https://github.com/Tuinity/Moonrise/issues"
sources = "https://github.com/Tuinity/Moonrise"
extra.put("discord", "https://discord.gg/tuinity")
homepage = "https://www.curseforge.com/minecraft/mc-mods/moonrise"
}
breaks("notenoughcrashes")
breaks("starlight")
breaks("c2me")
license("GPL-3.0-only")
icon("assets/moonrise/icon.png")
environment = Environment.ANY
clientEntrypoint("ca.spottedleaf.moonrise.fabric.MoonriseFabricClient")
entrypoint("modmenu", "ca.spottedleaf.moonrise.fabric.MoonriseModMenuHook")
mixin("moonrise.mixins.json")
mixin("moonrise-fabric.mixins.json")
accessWidener = "moonrise.accesswidener"
depends("fabricloader", ">=${loader_version}")
depends("minecraft", ">1.21.4 <1.21.6")
depends("fabric-command-api-v2", "*")
custom.put(
"ferritecore:disabled_options",
simpleCustomValueList(String, ["replaceNeighborLookup", "replacePropertyMap"])
)
custom.put(
"lithium:options",
simpleCustomValueMap(
String,
Boolean,
[
"mixin.ai.poi": false,
"mixin.alloc.deep_passengers": false,
"mixin.alloc.entity_tracker": false,
"mixin.block.flatten_states": false,
"mixin.chunk.entity_class_groups": false,
"mixin.chunk.no_validation": false,
"mixin.collections.chunk_tickets": false,
"mixin.collections.entity_ticking": false,
"mixin.entity.collisions.intersection": false,
"mixin.entity.collisions.movement": false,
"mixin.entity.collisions.unpushable_cramming": false,
"mixin.entity.replace_entitytype_predicates": false,
"mixin.math.fast_blockpos": false,
"mixin.math.fast_util": false,
"mixin.minimal_nonvanilla.collisions.empty_space": false,
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
"mixin.shapes.blockstate_cache": false,
"mixin.shapes.optimized_matching": false,
"mixin.shapes.specialized_shapes": false,
"mixin.util.block_tracking": false,
"mixin.util.entity_movement_tracking": false,
"mixin.world.block_entity_ticking": false,
"mixin.world.chunk_access": false,
"mixin.world.explosions.block_raycast": false,
"mixin.world.temperature_cache": false,
"mixin.world.tick_scheduler": false,
]
)
)
}
publishMods {
file = remapJar.archiveFile
modLoaders = ["fabric"]
modrinth {
@@ -60,7 +123,56 @@ publishMods {
incompatible(
"not-enough-crashes",
"starlight",
"c2me-fabric"
"c2me"
)
}
}
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;
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrays;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.BlockPos;
@@ -14,7 +17,9 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
@@ -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.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
public final class FabricHooks implements PlatformHooks {
public final class FabricHooks extends BaseChunkSystemHooks implements PlatformHooks {
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
@@ -111,13 +117,43 @@ public final class FabricHooks implements PlatformHooks {
@Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) {
final Collection<EnderDragonPart> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final EnderDragonPart part : parts) {
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
into.add(part);
}
}
}
@Override
public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest, final AABB boundingBox,
final Predicate<? super T> predicate, final List<? super T> into, final int maxCount) {
if (into.size() >= maxCount) {
// fix neoforge issue: do not add if list is already full
return;
}
final Collection<EnderDragonPart> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final EnderDragonPart part : parts) {
if (!part.getBoundingBox().intersects(boundingBox)) {
continue;
}
final T casted = (T)entityTypeTest.tryCast(part);
if (casted != null && (predicate == null || predicate.test(casted))) {
into.add(casted);
if (into.size() >= maxCount) {
break;
}
}
}
}
@Override
@@ -222,4 +258,9 @@ public final class FabricHooks implements PlatformHooks {
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
@Override
public long[] getCounterTypesUncached(final TicketType type) {
return type == TicketType.FORCED ? new long[] { ChunkSystemTicketType.COUNTER_TYPE_FORCED } : LongArrays.EMPTY_ARRAY;
}
}

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

@@ -1,74 +0,0 @@
{
"schemaVersion": 1,
"id": "moonrise",
"version": "${version}",
"name": "Moonrise",
"description": "Optimisation mod for the dedicated and integrated server.",
"authors": [
"Spottedleaf"
],
"contact": {
"issues": "https://github.com/Tuinity/Moonrise/issues",
"sources": "https://github.com/Tuinity/Moonrise",
"discord": "https://discord.gg/tuinity",
"homepage": "https://www.curseforge.com/minecraft/mc-mods/moonrise"
},
"breaks": {
"notenoughcrashes": "*",
"starlight": "*",
"c2me": "*"
},
"license": "GPL-3.0-only",
"icon": "assets/moonrise/icon.png",
"environment": "*",
"entrypoints": {
"modmenu": [
"ca.spottedleaf.moonrise.fabric.MoonriseModMenuHook"
],
"client": [
"ca.spottedleaf.moonrise.fabric.MoonriseFabricClient"
]
},
"mixins": [
"moonrise.mixins.json",
"moonrise-fabric.mixins.json"
],
"accessWidener": "moonrise.accesswidener",
"depends": {
"fabricloader": ">=${loader_version}",
"minecraft": ">1.21.1 <1.21.4",
"fabric-command-api-v2": "*"
},
"custom": {
"lithium:options": {
"mixin.ai.poi": false,
"mixin.alloc.chunk_ticking": false,
"mixin.alloc.deep_passengers": false,
"mixin.alloc.entity_tracker": false,
"mixin.block.flatten_states": false,
"mixin.chunk.entity_class_groups": false,
"mixin.chunk.no_validation": false,
"mixin.collections.chunk_tickets": false,
"mixin.collections.entity_ticking": false,
"mixin.entity.collisions.intersection": false,
"mixin.entity.collisions.movement": false,
"mixin.entity.collisions.unpushable_cramming": false,
"mixin.entity.replace_entitytype_predicates": false,
"mixin.math.fast_blockpos": false,
"mixin.math.fast_util": false,
"mixin.minimal_nonvanilla.collisions.empty_space": false,
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
"mixin.shapes.blockstate_cache": false,
"mixin.shapes.optimized_matching": false,
"mixin.shapes.specialized_shapes": false,
"mixin.util.block_tracking": false,
"mixin.util.entity_movement_tracking": false,
"mixin.world.block_entity_ticking": false,
"mixin.world.chunk_access": false,
"mixin.world.explosions.block_raycast": false,
"mixin.world.explosions.cache_exposure": false,
"mixin.world.temperature_cache": false,
"mixin.world.tick_scheduler": false
}
}
}

View File

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

View File

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

View File

@@ -1,21 +1,26 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.3
loader_version=0.16.7
supported_minecraft_versions=1.21.3
neoforge_version=21.3.0-beta
fabric_api_version=0.107.0+1.21.3
snakeyaml_version=2.2
concurrentutil_version=0.0.2-SNAPSHOT
cloth_version=16.0.141
modmenu_version=12.0.0-beta.1
minecraft_version=1.21.5
loader_version=0.16.14
supported_minecraft_versions=1.21.5
neoforge_version=21.5.65-beta
neoform_version=1.21.5-20250325.162830
fabric_api_version=0.123.0+1.21.5
snakeyaml_version=2.3
concurrentutil_version=0.0.3
yamlconfig_version=1.0.2
cloth_version=18.0.145
modmenu_version=14.0.0-rc.2
junit_version=5.11.3
# version ids from modrinth
fabric_lithium_version=mc1.21.1-0.14.0-beta.1
neo_lithium_version=BrMIoIMv
fabric_lithium_version=nhc57Td2
neo_lithium_version=P5VT33Jo
# Mod Properties
mod_version=0.2.0-beta.3
mod_version=0.3.0-SNAPSHOT
maven_group=ca.spottedleaf.moonrise
archives_base_name=moonrise

Binary file not shown.

View File

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

3
gradlew vendored
View File

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

View File

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

View File

@@ -1,9 +1,11 @@
import net.fabricmc.loom.util.aw2at.Aw2At
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import net.neoforged.moddevgradle.internal.RunGameTask
plugins {
id("xyz.jpenilla.quiet-architectury-loom")
id("net.neoforged.moddev")
id 'maven-publish'
id 'com.gradleup.shadow'
id 'xyz.jpenilla.resource-factory-neoforge-convention'
}
repositories {
@@ -13,41 +15,145 @@ repositories {
}
}
configurations.implementation {
extendsFrom(configurations.shadow)
}
def aw2at = Aw2AtTask.configureDefault(
getProject(),
rootProject.layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
sourceSets.main
)
dependencies {
add('shadow', project([path: ":", configuration: "namedElements"]))
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
modImplementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
}
processResources {
inputs.property "version", project.version
filesMatching("META-INF/neoforge.mods.toml") {
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
neoForge {
version = rootProject.neoforge_version
validateAccessTransformers = true
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
mods {
moonrise {
sourceSet sourceSets.main
sourceSet rootProject.sourceSets.main
}
}
runs {
client {
client()
}
server {
server()
}
}
unitTest {
enable()
testedMod = mods.moonrise
}
}
shadowJar {
dependencies {
runtimeOnly(project(":").sourceSets.main.output)
shadow(project(":"))
compileOnly(project(":"))
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
additionalRuntimeClasspath libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
implementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
jarJar "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
}
tasks.jar {
archiveClassifier = "dev"
}
tasks.shadowJar {
archiveClassifier = "dev-all"
destinationDirectory = layout.buildDirectory.dir("libs")
configurations = [project.configurations.shadow]
relocate 'ca.spottedleaf.concurrentutil', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.concurrentutil'
relocate 'ca.spottedleaf.yamlconfig', 'ca.spottedleaf.moonrise.libs.ca.spottedleaf.yamlconfig'
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
}
neoForgeModsToml {
license = "GPLv3"
issueTrackerUrl = "https://github.com/Tuinity/Moonrise"
showAsResourcePack = false
mods.register("moonrise") {
setConventionsFromProjectMeta(project)
displayName = "Moonrise"
displayUrl = "https://github.com/Tuinity/Moonrise"
authors = "Spottedleaf"
description = "Optimisation mod for the dedicated and integrated server."
logoFile = "assets/moonrise/icon.png"
custom.put(
"ferritecore:disabled_options",
simpleCustomValueList(String, ["replaceNeighborLookup", "replacePropertyMap"])
)
dependencies {
required("neoforge", "[21.5,)")
required("minecraft", "1.21.5")
incompatible("notenoughcrashes")
incompatible("starlight")
incompatible("c2me")
}
}
mixins(
"moonrise.mixins.json",
"moonrise-neoforge.mixins.json"
)
custom.put(
"lithium:options",
simpleCustomValueMap(
String,
Boolean,
[
"mixin.ai.poi": false,
"mixin.alloc.deep_passengers": false,
"mixin.alloc.entity_tracker": false,
"mixin.block.flatten_states": false,
"mixin.chunk.entity_class_groups": false,
"mixin.chunk.no_validation": false,
"mixin.collections.chunk_tickets": false,
"mixin.collections.entity_ticking": false,
"mixin.entity.collisions.intersection": false,
"mixin.entity.collisions.movement": false,
"mixin.entity.collisions.unpushable_cramming": false,
"mixin.entity.replace_entitytype_predicates": false,
"mixin.math.fast_blockpos": false,
"mixin.math.fast_util": false,
"mixin.minimal_nonvanilla.collisions.empty_space": false,
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
"mixin.shapes.blockstate_cache": false,
"mixin.shapes.optimized_matching": false,
"mixin.shapes.specialized_shapes": false,
"mixin.util.block_tracking": false,
"mixin.util.entity_movement_tracking": false,
"mixin.world.block_entity_ticking": false,
"mixin.world.chunk_access": false,
"mixin.world.explosions.block_raycast": false,
"mixin.world.temperature_cache": false,
"mixin.world.tick_scheduler": false,
]
)
)
}
publishMods {
file = productionJar.archiveFile
modLoaders = ["neoforge"]
modrinth {
@@ -65,3 +171,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;
import ca.spottedleaf.moonrise.common.util.BaseChunkSystemHooks;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicketType;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
@@ -13,6 +16,7 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
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.level.ChunkDataEvent;
import net.neoforged.neoforge.event.level.ChunkEvent;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
public final class NeoForgeHooks implements PlatformHooks {
public final class NeoForgeHooks extends BaseChunkSystemHooks implements PlatformHooks {
@Override
public String getBrand() {
@@ -114,7 +119,12 @@ public final class NeoForgeHooks implements PlatformHooks {
@Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) {
for (final PartEntity<?> part : world.getPartEntities()) {
final Collection<PartEntity<?>> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final PartEntity<?> part : parts) {
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
into.add(part);
}
@@ -129,9 +139,18 @@ public final class NeoForgeHooks implements PlatformHooks {
return;
}
for (final PartEntity<?> part : world.getPartEntities()) {
final Collection<PartEntity<?>> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final PartEntity<?> part : parts) {
if (!part.getBoundingBox().intersects(boundingBox)) {
continue;
}
final T casted = (T)entityTypeTest.tryCast(part);
if (casted != null && casted.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(casted))) {
if (casted != null && (predicate == null || predicate.test(casted))) {
into.add(casted);
if (into.size() >= maxCount) {
break;
@@ -245,4 +264,18 @@ public final class NeoForgeHooks implements PlatformHooks {
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
@Override
public long[] getCounterTypesUncached(final TicketType type) {
final LongArrayList ret = new LongArrayList();
if (type == TicketType.FORCED) {
ret.add(ChunkSystemTicketType.COUNTER_TYPE_FORCED);
}
if (type.forceNaturalSpawning()) {
ret.add(ChunkSystemTicketType.COUNTER_TYPER_NATURAL_SPAWNING_FORCED);
}
return ret.toLongArray();
}
}

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

@@ -1,82 +0,0 @@
modLoader = "javafml"
loaderVersion = "[1,)"
license = "GPLv3"
issueTrackerURL = "https://github.com/Tuinity/Moonrise"
showAsResourcePack = false
logoFile = "assets/moonrise/icon.png"
[[mods]]
modId = "moonrise"
version = "${version}"
displayName = "Moonrise"
displayURL = "https://github.com/Tuinity/Moonrise"
authors = "Spottedleaf"
description = "Optimisation mod for the dedicated and integrated server."
displayTest = "IGNORE_ALL_VERSION"
[[dependencies.moonrise]]
modId = "neoforge"
type = "required"
versionRange = "[21.0,)"
ordering = "NONE"
side = "BOTH"
[[dependencies.moonrise]]
modId = "minecraft"
type = "required"
versionRange = "(1.21.1,1.21.4)"
ordering = "NONE"
side = "BOTH"
[[dependencies.moonrise]]
modId = "notenoughcrashes"
type = "incompatible"
[[dependencies.moonrise]]
modId = "starlight"
type = "incompatible"
[[dependencies.moonrise]]
# unofficial lithium port
modId = "canary"
type = "incompatible"
[[dependencies.moonrise]]
modId = "c2me"
type = "incompatible"
[[mixins]]
config = "moonrise.mixins.json"
[[mixins]]
config = "moonrise-neoforge.mixins.json"
[mods."lithium:options"]
"mixin.ai.poi" = false
"mixin.alloc.chunk_ticking" = false
"mixin.alloc.deep_passengers" = false
"mixin.alloc.entity_tracker" = false
"mixin.block.flatten_states" = false
"mixin.chunk.entity_class_groups" = false
"mixin.chunk.no_validation" = false
"mixin.collections.chunk_tickets" = false
"mixin.collections.entity_ticking" = false
"mixin.entity.collisions.intersection" = false
"mixin.entity.collisions.movement" = false
"mixin.entity.collisions.unpushable_cramming" = false
"mixin.entity.replace_entitytype_predicates" = false
"mixin.math.fast_blockpos" = false
"mixin.math.fast_util" = false
"mixin.minimal_nonvanilla.collisions.empty_space" = false
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets" = false
"mixin.shapes.blockstate_cache" = false
"mixin.shapes.optimized_matching" = false
"mixin.shapes.specialized_shapes" = false
"mixin.util.block_tracking" = false
"mixin.util.entity_movement_tracking" = false
"mixin.world.block_entity_ticking" = false
"mixin.world.chunk_access" = false
"mixin.world.explosions.block_raycast" = false
"mixin.world.explosions.cache_exposure" = false
"mixin.world.temperature_cache" = false
"mixin.world.tick_scheduler" = false

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.common;
import ca.spottedleaf.moonrise.common.util.ChunkSystemHooks;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import net.minecraft.core.BlockPos;
@@ -8,7 +9,6 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
@@ -24,7 +24,7 @@ import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Predicate;
public interface PlatformHooks {
public interface PlatformHooks extends ChunkSystemHooks {
public static PlatformHooks get() {
return Holder.INSTANCE;
}
@@ -64,8 +64,6 @@ public interface PlatformHooks {
public void entityMove(final Entity entity, final long oldSection, final long newSection);
public boolean screenEntity(final ServerLevel world, final Entity entity, final boolean fromDisk, final boolean event);
public boolean configFixMC224294();
public boolean configAutoConfigSendDistance();

View File

@@ -1,7 +0,0 @@
package ca.spottedleaf.moonrise.common.config;
public interface InitialiseHook {
public void initialise();
}

View File

@@ -1,11 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter;
import java.lang.reflect.Type;
public abstract class TypeAdapter<T, S> {
public abstract T deserialize(final TypeAdapterRegistry registry, final Object input, final Type type);
public abstract S serialize(final TypeAdapterRegistry registry, final T value, final Type type);
}

View File

@@ -1,307 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter;
import ca.spottedleaf.moonrise.common.config.InitialiseHook;
import ca.spottedleaf.moonrise.common.config.adapter.collection.CollectionTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.collection.ListTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.collection.SortedMapTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.BooleanTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.ByteTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.DoubleTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.FloatTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.IntegerTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.LongTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.ShortTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.StringTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.type.BigDecimalTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.type.BigIntegerTypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.type.DurationTypeAdapter;
import ca.spottedleaf.moonrise.common.config.annotation.Adaptable;
import ca.spottedleaf.moonrise.common.config.annotation.Serializable;
import ca.spottedleaf.moonrise.common.config.type.Duration;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public final class TypeAdapterRegistry {
private final Map<Class<?>, TypeAdapter<?, ?>> adapters = new HashMap<>();
{
this.adapters.put(boolean.class, BooleanTypeAdapter.INSTANCE);
this.adapters.put(byte.class, ByteTypeAdapter.INSTANCE);
this.adapters.put(short.class, ShortTypeAdapter.INSTANCE);
this.adapters.put(int.class, IntegerTypeAdapter.INSTANCE);
this.adapters.put(long.class, LongTypeAdapter.INSTANCE);
this.adapters.put(float.class, FloatTypeAdapter.INSTANCE);
this.adapters.put(double.class, DoubleTypeAdapter.INSTANCE);
this.adapters.put(Boolean.class, BooleanTypeAdapter.INSTANCE);
this.adapters.put(Byte.class, ByteTypeAdapter.INSTANCE);
this.adapters.put(Short.class, ShortTypeAdapter.INSTANCE);
this.adapters.put(Integer.class, IntegerTypeAdapter.INSTANCE);
this.adapters.put(Long.class, LongTypeAdapter.INSTANCE);
this.adapters.put(Float.class, FloatTypeAdapter.INSTANCE);
this.adapters.put(Double.class, DoubleTypeAdapter.INSTANCE);
this.adapters.put(String.class, StringTypeAdapter.INSTANCE);
this.adapters.put(Collection.class, CollectionTypeAdapter.INSTANCE);
this.adapters.put(List.class, ListTypeAdapter.INSTANCE);
this.adapters.put(Map.class, SortedMapTypeAdapter.SORTED_CASE_INSENSITIVE);
this.adapters.put(LinkedHashMap.class, SortedMapTypeAdapter.SORTED_CASE_INSENSITIVE);
this.adapters.put(BigInteger.class, BigIntegerTypeAdapter.INSTANCE);
this.adapters.put(BigDecimal.class, BigDecimalTypeAdapter.INSTANCE);
this.adapters.put(Duration.class, DurationTypeAdapter.INSTANCE);
}
public TypeAdapter<?, ?> putAdapter(final Class<?> clazz, final TypeAdapter<?, ?> adapter) {
return this.adapters.put(clazz, adapter);
}
public TypeAdapter<?, ?> getAdapter(final Class<?> clazz) {
return this.adapters.get(clazz);
}
public Object deserialize(final Object input, final Type type) {
TypeAdapter<?, ?> adapter = null;
if (type instanceof Class<?> clazz) {
adapter = this.adapters.get(clazz);
}
if (adapter == null && (type instanceof ParameterizedType parameterizedType)) {
adapter = this.adapters.get((Class<?>)parameterizedType.getRawType());
}
if (adapter == null) {
throw new IllegalArgumentException("No adapter for " + input.getClass() + " with type " + type);
}
return ((TypeAdapter)adapter).deserialize(this, input, type);
}
public Object serialize(final Object input, final Type type) {
TypeAdapter<?, ?> adapter = null;
if (type instanceof Class<?> clazz) {
adapter = this.adapters.get(clazz);
}
if (adapter == null && (type instanceof ParameterizedType parameterizedType)) {
adapter = this.adapters.get((Class<?>)parameterizedType.getRawType());
}
if (adapter == null) {
adapter = this.adapters.get(input.getClass());
}
if (adapter == null) {
throw new IllegalArgumentException("No adapter for " + input.getClass() + " with type " + type);
}
return ((TypeAdapter)adapter).serialize(this, input, type);
}
public <T> TypeAdapter<T, Map<Object, Object>> makeAdapter(final Class<? extends T> clazz) throws Exception {
final TypeAdapter<T, Map<Object, Object>> ret = new AutoTypeAdapter<>(this, clazz);
this.putAdapter(clazz, ret);
return ret;
}
public <T> void callInitialisers(final T object) {
if (object == null) {
return;
}
final TypeAdapter<?, ?> adapter = this.getAdapter(object.getClass());
if (!(adapter instanceof AutoTypeAdapter<?> autoTypeAdapter)) {
return;
}
((AutoTypeAdapter<T>)autoTypeAdapter).callInitialisers(object);
}
private static final class AutoTypeAdapter<T> extends TypeAdapter<T, Map<Object, Object>> {
private final TypeAdapterRegistry registry;
private final Constructor<? extends T> constructor;
private final SerializableField[] fields;
public AutoTypeAdapter(final TypeAdapterRegistry registry, final Class<? extends T> clazz) throws Exception {
this.registry = registry;
this.constructor = clazz.getConstructor();
this.fields = findSerializableFields(registry, clazz);
}
private static TypeAdapter<?, ?> findOrMakeAdapter(final TypeAdapterRegistry registry, final Class<?> clazz) throws Exception {
final TypeAdapter<?, ?> ret = registry.getAdapter(clazz);
if (ret != null) {
return ret;
}
for (final Annotation annotation : clazz.getAnnotations()) {
if (annotation instanceof Adaptable adaptable) {
return registry.makeAdapter(clazz);
}
}
throw new IllegalArgumentException("No type adapter for " + clazz + " (Forgot @Adaptable?)");
}
private static String makeSerializedKey(final String input) {
final StringBuilder ret = new StringBuilder();
for (final char c : input.toCharArray()) {
if (!Character.isUpperCase(c)) {
ret.append(c);
continue;
}
ret.append('-');
ret.append(Character.toLowerCase(c));
}
return ret.toString();
}
private static record SerializableField(
Field field,
boolean required,
String comment,
TypeAdapter<?, ?> adapter,
boolean serialize,
String serializedKey
) {}
private static SerializableField[] findSerializableFields(final TypeAdapterRegistry registry, Class<?> clazz) throws Exception {
final List<SerializableField> ret = new ArrayList<>();
do {
for (final Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
for (final Annotation annotation : field.getAnnotations()) {
if (!(annotation instanceof Serializable serializable)) {
continue;
}
final TypeAdapter<?, ?> adapter;
if (serializable.adapter() != TypeAdapter.class) {
adapter = serializable.adapter().getConstructor().newInstance();
} else {
adapter = findOrMakeAdapter(registry, field.getType());
}
String serializedKey = serializable.serializedKey();
if (serializedKey.isEmpty()) {
serializedKey = makeSerializedKey(field.getName());
}
ret.add(new SerializableField(
field, serializable.required(), serializable.comment(), adapter,
serializable.serialize(), serializedKey
));
}
}
} while ((clazz = clazz.getSuperclass()) != Object.class);
ret.sort((final SerializableField c1, final SerializableField c2) -> {
return c1.serializedKey.compareTo(c2.serializedKey);
});
return ret.toArray(new SerializableField[0]);
}
@Override
public T deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (!(input instanceof Map<?,?> inputMap)) {
throw new IllegalArgumentException("Not a map type: " + input.getClass());
}
try {
final T ret = this.constructor.newInstance();
for (final SerializableField field : this.fields) {
final Object fieldValue = inputMap.get(field.serializedKey);
if (fieldValue == null) {
if (field.required) {
throw new IllegalArgumentException("Missing required field '" + field.serializedKey + "' in " + this.constructor.getDeclaringClass());
}
continue;
}
field.field.set(ret, field.adapter.deserialize(registry, fieldValue, field.field.getGenericType()));
}
return ret;
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
@Override
public Map<Object, Object> serialize(final TypeAdapterRegistry registry, final T value, final Type type) {
final LinkedHashMap<Object, Object> ret = new LinkedHashMap<>();
for (final SerializableField field : this.fields) {
if (!field.serialize) {
continue;
}
final Object fieldValue;
try {
fieldValue = field.field.get(value);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
if (fieldValue != null) {
ret.put(
field.comment.isBlank() ? field.serializedKey : new CommentedData(field.comment, field.serializedKey),
((TypeAdapter)field.adapter).serialize(
registry, fieldValue, field.field.getGenericType()
)
);
}
}
return ret;
}
public void callInitialisers(final T value) {
for (final SerializableField field : this.fields) {
final Object fieldValue;
try {
fieldValue = field.field.get(value);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
if (fieldValue instanceof InitialiseHook initialiseHook) {
initialiseHook.initialise();
}
this.registry.callInitialisers(fieldValue);
}
}
}
public static final class CommentedData {
public final String comment;
public final Object data;
public CommentedData(final String comment, final Object data) {
this.comment = comment;
this.data = data;
}
}
}

View File

@@ -1,46 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.collection;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import ca.spottedleaf.moonrise.common.config.adapter.primitive.StringTypeAdapter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public final class CollectionTypeAdapter extends TypeAdapter<Collection<Object>, List<Object>> {
public static final CollectionTypeAdapter INSTANCE = new CollectionTypeAdapter();
@Override
public Collection<Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (!(type instanceof ParameterizedType parameterizedType)) {
throw new IllegalArgumentException("Collection field must specify generic type");
}
final Type elementType = parameterizedType.getActualTypeArguments()[0];
if (input instanceof Collection<?> collection) {
final List<Object> ret = new ArrayList<>(collection.size());
for (final Object v : collection) {
ret.add(registry.deserialize(v, elementType));
}
return ret;
}
throw new IllegalArgumentException("Not a collection type: " + input.getClass());
}
@Override
public List<Object> serialize(final TypeAdapterRegistry registry, final Collection<Object> value, final Type type) {
final List<Object> ret = new ArrayList<>(value.size());
final Type elementType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[0] : null;
for (final Object v : value) {
ret.add(registry.serialize(v, elementType == null ? v.getClass() : elementType));
}
return ret;
}
}

View File

@@ -1,45 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.collection;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public final class ListTypeAdapter extends TypeAdapter<List<Object>, List<Object>> {
public static final ListTypeAdapter INSTANCE = new ListTypeAdapter();
@Override
public List<Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (!(type instanceof ParameterizedType parameterizedType)) {
throw new IllegalArgumentException("Collection field must specify generic type");
}
final Type elementType = parameterizedType.getActualTypeArguments()[0];
if (input instanceof Collection<?> collection) {
final List<Object> ret = new ArrayList<>(collection.size());
for (final Object v : collection) {
ret.add(registry.deserialize(v, elementType));
}
return ret;
}
throw new IllegalArgumentException("Not a collection type: " + input.getClass());
}
@Override
public List<Object> serialize(final TypeAdapterRegistry registry, final List<Object> value, final Type type) {
final List<Object> ret = new ArrayList<>(value.size());
final Type elementType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[0] : null;
for (final Object v : value) {
ret.add(registry.serialize(v, elementType));
}
return ret;
}
}

View File

@@ -1,59 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.collection;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public final class SortedMapTypeAdapter extends TypeAdapter<Map<String, Object>, Map<String, Object>> {
public static final SortedMapTypeAdapter SORTED_CASE_INSENSITIVE = new SortedMapTypeAdapter(String.CASE_INSENSITIVE_ORDER);
public static final SortedMapTypeAdapter SORTED_CASE_SENSITIVE = new SortedMapTypeAdapter(null);
private final Comparator<String> keyComparator;
public SortedMapTypeAdapter(final Comparator<String> keyComparator) {
this.keyComparator = keyComparator;
}
@Override
public Map<String, Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (!(type instanceof ParameterizedType parameterizedType)) {
throw new IllegalArgumentException("Collection field must specify generic type");
}
final Type valueType = parameterizedType.getActualTypeArguments()[1];
if (input instanceof Map<?,?> inputMap) {
final Map<String, Object> castedInput = (Map<String, Object>)inputMap;
final TreeMap<String, Object> ret = new TreeMap<>(this.keyComparator);
for (final Map.Entry<String, Object> entry : castedInput.entrySet()) {
ret.put(entry.getKey(), registry.deserialize(entry.getValue(), valueType));
}
// transform to linked so that get() is O(1)
return new LinkedHashMap<>(ret);
}
throw new IllegalArgumentException("Not a map type: " + input.getClass());
}
@Override
public Map<String, Object> serialize(final TypeAdapterRegistry registry, final Map<String, Object> value, final Type type) {
final TreeMap<String, Object> ret = new TreeMap<>(this.keyComparator);
final Type valueType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[1] : null;
for (final Map.Entry<String, Object> entry : value.entrySet()) {
ret.put(entry.getKey(), registry.serialize(entry.getValue(), valueType));
}
// transform to linked so that get() is O(1)
return new LinkedHashMap<>(ret);
}
}

View File

@@ -1,47 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.collection;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
public final class UnsortedMapTypeAdapter extends TypeAdapter<Map<String, Object>, Map<String, Object>> {
public static final UnsortedMapTypeAdapter INSTANCE = new UnsortedMapTypeAdapter();
@Override
public Map<String, Object> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (!(type instanceof ParameterizedType parameterizedType)) {
throw new IllegalArgumentException("Collection field must specify generic type");
}
final Type valueType = parameterizedType.getActualTypeArguments()[1];
if (input instanceof Map<?,?> inputMap) {
final Map<String, Object> castedInput = (Map<String, Object>)inputMap;
final LinkedHashMap<String, Object> ret = new LinkedHashMap<>();
for (final Map.Entry<String, Object> entry : castedInput.entrySet()) {
ret.put(entry.getKey(), registry.deserialize(entry.getValue(), valueType));
}
return ret;
}
throw new IllegalArgumentException("Not a map type: " + input.getClass());
}
@Override
public Map<String, Object> serialize(final TypeAdapterRegistry registry, final Map<String, Object> value, final Type type) {
final LinkedHashMap<String, Object> ret = new LinkedHashMap<>();
final Type valueType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[1] : null;
for (final Map.Entry<String, Object> entry : value.entrySet()) {
ret.put(entry.getKey(), registry.serialize(entry.getValue(), valueType));
}
return ret;
}
}

View File

@@ -1,33 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
public final class BooleanTypeAdapter extends TypeAdapter<Boolean, Boolean> {
public static final BooleanTypeAdapter INSTANCE = new BooleanTypeAdapter();
@Override
public Boolean deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Boolean ret) {
return ret;
}
if (input instanceof String str) {
if (str.equalsIgnoreCase("false")) {
return Boolean.FALSE;
}
if (str.equalsIgnoreCase("true")) {
return Boolean.TRUE;
}
throw new IllegalArgumentException("Not a boolean: " + str);
}
throw new IllegalArgumentException("Not a boolean type: " + input.getClass());
}
@Override
public Boolean serialize(final TypeAdapterRegistry registry, final Boolean value, final Type type) {
return value;
}
}

View File

@@ -1,36 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
import java.math.BigInteger;
public final class ByteTypeAdapter extends TypeAdapter<Byte, Byte> {
public static final ByteTypeAdapter INSTANCE = new ByteTypeAdapter();
private static Byte cast(final Object original, final long value) {
if (value < (long)Byte.MIN_VALUE || value > (long)Byte.MAX_VALUE) {
throw new IllegalArgumentException("Byte value is out of range: " + original.toString());
}
return Byte.valueOf((byte)value);
}
@Override
public Byte deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
// note: silently discard floating point significand
return cast(input, number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue());
}
if (input instanceof String string) {
return cast(input, (long)Double.parseDouble(string));
}
throw new IllegalArgumentException("Not a byte type: " + input.getClass());
}
@Override
public Byte serialize(final TypeAdapterRegistry registry, final Byte value, final Type type) {
return value;
}
}

View File

@@ -1,27 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
public final class DoubleTypeAdapter extends TypeAdapter<Double, Double> {
public static final DoubleTypeAdapter INSTANCE = new DoubleTypeAdapter();
@Override
public Double deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
return Double.valueOf(number.doubleValue());
}
if (input instanceof String string) {
return Double.valueOf(Double.parseDouble(string));
}
throw new IllegalArgumentException("Not a byte type: " + input.getClass());
}
@Override
public Double serialize(final TypeAdapterRegistry registry, final Double value, final Type type) {
return value;
}
}

View File

@@ -1,35 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
public final class FloatTypeAdapter extends TypeAdapter<Float, Float> {
public static final FloatTypeAdapter INSTANCE = new FloatTypeAdapter();
private static Float cast(final Object original, final double value) {
if (value < -(double)Float.MAX_VALUE || value > (double)Float.MAX_VALUE) {
throw new IllegalArgumentException("Byte value is out of range: " + original.toString());
}
// note: silently ignore precision loss
return Float.valueOf((float)value);
}
@Override
public Float deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
return cast(input, number.doubleValue());
}
if (input instanceof String string) {
return cast(input, Double.parseDouble(string));
}
throw new IllegalArgumentException("Not a byte type: " + input.getClass());
}
@Override
public Float serialize(final TypeAdapterRegistry registry, final Float value, final Type type) {
return value;
}
}

View File

@@ -1,36 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
import java.math.BigInteger;
public final class IntegerTypeAdapter extends TypeAdapter<Integer, Integer> {
public static final IntegerTypeAdapter INSTANCE = new IntegerTypeAdapter();
private static Integer cast(final Object original, final long value) {
if (value < (long)Integer.MIN_VALUE || value > (long)Integer.MAX_VALUE) {
throw new IllegalArgumentException("Integer value is out of range: " + original.toString());
}
return Integer.valueOf((int)value);
}
@Override
public Integer deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
// note: silently discard floating point significand
return cast(input, number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue());
}
if (input instanceof String string) {
return cast(input, (long)Double.parseDouble(string));
}
throw new IllegalArgumentException("Not an integer type: " + input.getClass());
}
@Override
public Integer serialize(final TypeAdapterRegistry registry, final Integer value, final Type type) {
return value;
}
}

View File

@@ -1,33 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
import java.math.BigInteger;
public final class LongTypeAdapter extends TypeAdapter<Long, Long> {
public static final LongTypeAdapter INSTANCE = new LongTypeAdapter();
@Override
public Long deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
// note: silently discard floating point significand
return number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue();
}
if (input instanceof String string) {
try {
return Long.valueOf(Long.parseLong(string));
} catch (final NumberFormatException ex) {
return Long.valueOf((long)Double.parseDouble(string));
}
}
throw new IllegalArgumentException("Not a long type: " + input.getClass());
}
@Override
public Long serialize(final TypeAdapterRegistry registry, final Long value, final Type type) {
return value;
}
}

View File

@@ -1,36 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
import java.math.BigInteger;
public final class ShortTypeAdapter extends TypeAdapter<Short, Short> {
public static final ShortTypeAdapter INSTANCE = new ShortTypeAdapter();
private static Short cast(final Object original, final long value) {
if (value < (long)Short.MIN_VALUE || value > (long)Short.MAX_VALUE) {
throw new IllegalArgumentException("Short value is out of range: " + original.toString());
}
return Short.valueOf((short)value);
}
@Override
public Short deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
// note: silently discard floating point significand
return cast(input, number instanceof BigInteger bigInteger ? bigInteger.longValueExact() : number.longValue());
}
if (input instanceof String string) {
return cast(input, (long)Double.parseDouble(string));
}
throw new IllegalArgumentException("Not a short type: " + input.getClass());
}
@Override
public Short serialize(final TypeAdapterRegistry registry, final Short value, final Type type) {
return value;
}
}

View File

@@ -1,29 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.primitive;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
public final class StringTypeAdapter extends TypeAdapter<String, String> {
public static final StringTypeAdapter INSTANCE = new StringTypeAdapter();
@Override
public String deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Boolean bool) {
return String.valueOf(bool.booleanValue());
}
if (input instanceof Number number) {
return number.toString();
}
if (input instanceof String string) {
return string;
}
throw new IllegalArgumentException("Not a string type: " + input.getClass());
}
@Override
public String serialize(final TypeAdapterRegistry registry, final String value, final Type type) {
return value;
}
}

View File

@@ -1,30 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.type;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
public final class BigDecimalTypeAdapter extends TypeAdapter<BigDecimal, String> {
public static final BigDecimalTypeAdapter INSTANCE = new BigDecimalTypeAdapter();
@Override
public BigDecimal deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
// safest to catch all number impls is to use toString
return new BigDecimal(number.toString());
}
if (input instanceof String string) {
return new BigDecimal(string);
}
throw new IllegalArgumentException("Not an BigDecimal type: " + input.getClass());
}
@Override
public String serialize(final TypeAdapterRegistry registry, final BigDecimal value, final Type type) {
return value.toString();
}
}

View File

@@ -1,37 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.type;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
public final class BigIntegerTypeAdapter extends TypeAdapter<BigInteger, String> {
public static final BigIntegerTypeAdapter INSTANCE = new BigIntegerTypeAdapter();
@Override
public BigInteger deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof Number number) {
if (number instanceof BigInteger bigInteger) {
return bigInteger;
}
// note: silently discard floating point significand
if (number instanceof BigDecimal bigDecimal) {
return bigDecimal.toBigInteger();
}
return BigInteger.valueOf(number.longValue());
}
if (input instanceof String string) {
return new BigDecimal(string).toBigInteger();
}
throw new IllegalArgumentException("Not an BigInteger type: " + input.getClass());
}
@Override
public String serialize(final TypeAdapterRegistry registry, final BigInteger value, final Type type) {
return value.toString();
}
}

View File

@@ -1,24 +0,0 @@
package ca.spottedleaf.moonrise.common.config.adapter.type;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import ca.spottedleaf.moonrise.common.config.type.Duration;
import java.lang.reflect.Type;
public final class DurationTypeAdapter extends TypeAdapter<Duration, String> {
public static final DurationTypeAdapter INSTANCE = new DurationTypeAdapter();
@Override
public Duration deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (!(input instanceof String string)) {
throw new IllegalArgumentException("Not a string: " + input.getClass());
}
return Duration.parse(string);
}
@Override
public String serialize(final TypeAdapterRegistry registry, final Duration value, final Type type) {
return value.toString();
}
}

View File

@@ -1,15 +0,0 @@
package ca.spottedleaf.moonrise.common.config.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation used on a class to indicate that its type adapter may automatically be generated. The class must have
* a public no-args constructor.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Adaptable {
}

View File

@@ -1,45 +0,0 @@
package ca.spottedleaf.moonrise.common.config.annotation;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Function;
/**
* Annotation indicating that a field should be deserialized or serialized from the config.
* By default, this annotation is not assumed.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Serializable {
/**
* Indicates whether this field is required to be present in the config. If the field is not present,
* and {@code required = true}, then an exception will be thrown during deserialization. If {@code required = false}
* and the field is not present, then the field value will remain unmodified.
*/
public boolean required() default false;
/**
* The comment to apply before the element when serializing.
*/
public String comment() default "";
/**
* Adapter override class. The class must have a public no-args constructor.
*/
public Class<? extends TypeAdapter> adapter() default TypeAdapter.class;
/**
* Whether to serialize the value to the config.
*/
public boolean serialize() default true;
/**
* When not empty, this value overrides the auto generated serialized key in the config.
*/
public String serializedKey() default "";
}

View File

@@ -1,178 +0,0 @@
package ca.spottedleaf.moonrise.common.config.config;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.comments.CommentLine;
import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.representer.Represent;
import org.yaml.snakeyaml.representer.Representer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public final class YamlConfig<T> {
public final TypeAdapterRegistry typeAdapters;
private final Class<? extends T> clazz;
public volatile T config;
private final Yaml yaml;
private final LoaderOptions loaderOptions;
private final DumperOptions dumperOptions;
public YamlConfig(final Class<? extends T> clazz, final T dfl) throws Exception {
this(clazz, dfl, new TypeAdapterRegistry());
}
public YamlConfig(final Class<? extends T> clazz, final T dfl, final TypeAdapterRegistry registry) throws Exception {
this.clazz = clazz;
this.config = dfl;
this.typeAdapters = registry;
this.typeAdapters.makeAdapter(clazz);
final LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setProcessComments(true);
final DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setProcessComments(true);
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
this.loaderOptions = loaderOptions;
this.dumperOptions = dumperOptions;
this.yaml = new Yaml(new YamlConstructor(loaderOptions), new YamlRepresenter(dumperOptions), dumperOptions, loaderOptions);
}
public void load(final File file) throws IOException {
try (final InputStream is = new BufferedInputStream(new FileInputStream(file))) {
this.load(is);
}
}
public void load(final InputStream is) throws IOException {
final Object serialized = this.yaml.load(new InputStreamReader(is, StandardCharsets.UTF_8));
this.config = (T)this.typeAdapters.deserialize(serialized, this.clazz);
}
public void save(final File file) throws IOException {
this.save(file, "");
}
public void save(final File file, final String header) throws IOException {
if (file.isDirectory()) {
throw new IOException("File is a directory");
}
final File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
final File tmp = new File(parent, file.getName() + ".tmp");
tmp.delete();
tmp.createNewFile();
try {
try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(tmp))) {
this.save(os, header);
}
try {
Files.move(tmp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} catch (final AtomicMoveNotSupportedException ex) {
Files.move(tmp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
} finally {
tmp.delete();
}
}
public void save(final OutputStream os) throws IOException {
os.write(this.saveToString().getBytes(StandardCharsets.UTF_8));
}
public void save(final OutputStream os, final String header) throws IOException {
os.write(this.saveToString(header).getBytes(StandardCharsets.UTF_8));
}
public String saveToString() {
return this.yaml.dump(this.typeAdapters.serialize(this.config, this.clazz));
}
public String saveToString(final String header) {
if (header.isBlank()) {
return this.saveToString();
}
final StringBuilder ret = new StringBuilder();
final String lineBreak = this.dumperOptions.getLineBreak().getString();
for (final String line : header.split("\n")) {
ret.append("# ").append(line.trim()).append(lineBreak);
}
ret.append(lineBreak);
return ret.append(this.saveToString()).toString();
}
public void callInitialisers() {
this.typeAdapters.callInitialisers(this.config);
}
private static final class YamlConstructor extends Constructor {
public YamlConstructor(final LoaderOptions loadingConfig) {
super(loadingConfig);
}
}
private static final class YamlRepresenter extends Representer {
public YamlRepresenter(final DumperOptions options) {
super(options);
this.representers.put(TypeAdapterRegistry.CommentedData.class, new CommentedDataRepresenter());
}
private final class CommentedDataRepresenter implements Represent {
@Override
public Node representData(final Object data0) {
final TypeAdapterRegistry.CommentedData commentedData = (TypeAdapterRegistry.CommentedData)data0;
final Node node = YamlRepresenter.this.representData(commentedData.data);
final List<CommentLine> comments = new ArrayList<>();
for (final String line : commentedData.comment.split("\n")) {
comments.add(new CommentLine(null, null, " ".concat(line.trim()), CommentType.BLOCK));
}
node.setBlockComments(comments);
return node;
}
}
}
}

View File

@@ -1,12 +1,12 @@
package ca.spottedleaf.moonrise.common.config.moonrise;
import ca.spottedleaf.moonrise.common.config.InitialiseHook;
import ca.spottedleaf.moonrise.common.config.annotation.Adaptable;
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
import ca.spottedleaf.moonrise.common.config.annotation.Serializable;
import ca.spottedleaf.moonrise.common.config.type.Duration;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.yamlconfig.InitialiseHook;
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
import ca.spottedleaf.yamlconfig.annotation.Serializable;
import ca.spottedleaf.yamlconfig.type.Duration;
@Adaptable
public final class MoonriseConfig {
@@ -251,4 +251,21 @@ public final class MoonriseConfig {
)
public boolean fixMC159283 = false;
}
@Serializable
public Misc misc = new Misc();
@Adaptable
public static final class Misc {
@Serializable(
serializedKey = "immediately-close-loading-screen",
comment = """
Whether the loading screen should be closed immediately when joining servers/SP worlds.
This will let you in game faster, but may result in getting in game before enough chunks are
loaded for rendering.
"""
)
public boolean immediatelyCloseLoadingScreen = false;
}
}

View File

@@ -1,38 +0,0 @@
package ca.spottedleaf.moonrise.common.config.moonrise.adapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapter;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import ca.spottedleaf.moonrise.common.config.moonrise.type.DefaultedValue;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public final class DefaultedTypeAdapter extends TypeAdapter<DefaultedValue<?>, Object> {
private static final String DEFAULT_STRING = "default";
@Override
public DefaultedValue<?> deserialize(final TypeAdapterRegistry registry, final Object input, final Type type) {
if (input instanceof String string && string.equalsIgnoreCase(DEFAULT_STRING)) {
return new DefaultedValue<>();
}
if (!(type instanceof ParameterizedType parameterizedType)) {
throw new IllegalArgumentException("DefaultedValue field must specify generic type");
}
final Type valueType = parameterizedType.getActualTypeArguments()[0];
return new DefaultedValue<>(registry.deserialize(input, valueType));
}
@Override
public Object serialize(final TypeAdapterRegistry registry, final DefaultedValue<?> value, final Type type) {
final Object raw = value.getValueRaw();
if (raw == null) {
return DEFAULT_STRING;
}
final Type valueType = type instanceof ParameterizedType parameterizedType ? parameterizedType.getActualTypeArguments()[0] : null;
return registry.serialize(raw, valueType);
}
}

View File

@@ -1,22 +0,0 @@
package ca.spottedleaf.moonrise.common.config.moonrise.type;
public final class DefaultedValue<T> {
private final T value;
public DefaultedValue() {
this(null);
}
public DefaultedValue(final T value) {
this.value = value;
}
public T getValueRaw() {
return value;
}
public T getOrDefault(final T dfl) {
return this.value != null ? this.value : dfl;
}
}

View File

@@ -1,76 +0,0 @@
package ca.spottedleaf.moonrise.common.config.type;
import java.math.BigDecimal;
public final class Duration {
private final String string;
private final long timeNS;
private Duration(final String string, final long timeNS) {
this.string = string;
this.timeNS = timeNS;
}
public static Duration parse(final String value) {
if (value.length() < 2) {
throw new IllegalArgumentException("Invalid duration: " + value);
}
final char last = value.charAt(value.length() - 1);
final long multiplier;
switch (last) {
case 's': {
multiplier = (1000L * 1000L * 1000L) * 1L;
break;
}
case 't': {
multiplier = (1000L * 1000L * 1000L) / 20L;
break;
}
case 'm': {
multiplier = (1000L * 1000L * 1000L) * 60L;
break;
}
case 'h': {
multiplier = (1000L * 1000L * 1000L) * 60L * 60L;
break;
}
case 'd': {
multiplier = (1000L * 1000L * 1000L) * 24L * 60L * 60L;
break;
}
default: {
throw new IllegalArgumentException("Duration must end with one of: [s, t, m, h, d]");
}
}
final BigDecimal parsed = new BigDecimal(value.substring(0, value.length() - 1))
.multiply(new BigDecimal(multiplier));
return new Duration(value, parsed.toBigInteger().longValueExact());
}
public long getTimeNS() {
return this.timeNS;
}
public long getTimeMS() {
return this.timeNS / (1000L * 1000L);
}
public long getTimeS() {
return this.timeNS / (1000L * 1000L * 1000L);
}
public long getTimeTicks() {
return this.timeNS / ((1000L * 1000L * 1000L) / (20L));
}
@Override
public String toString() {
return this.string;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@@ -1,11 +1,9 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import ca.spottedleaf.moonrise.common.config.config.YamlConfig;
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
import ca.spottedleaf.moonrise.common.config.moonrise.adapter.DefaultedTypeAdapter;
import ca.spottedleaf.moonrise.common.config.moonrise.type.DefaultedValue;
import ca.spottedleaf.yamlconfig.adapter.TypeAdapterRegistry;
import ca.spottedleaf.yamlconfig.config.YamlConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
@@ -18,8 +16,6 @@ public final class ConfigHolder {
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
private static final YamlConfig<MoonriseConfig> CONFIG;
static {
CONFIG_ADAPTERS.putAdapter(DefaultedValue.class, new DefaultedTypeAdapter());
try {
CONFIG = new YamlConfig<>(MoonriseConfig.class, new MoonriseConfig(), CONFIG_ADAPTERS);
} catch (final Exception ex) {
@@ -52,14 +48,6 @@ public final class ConfigHolder {
}
public static boolean reloadConfig() {
final boolean ret = reloadConfig0();
CONFIG.callInitialisers();
return ret;
}
private static boolean reloadConfig0() {
synchronized (CONFIG) {
if (CONFIG_FILE.exists()) {
try {
@@ -70,6 +58,8 @@ public final class ConfigHolder {
}
}
CONFIG.callInitialisers();
// write back any changes, or create if needed
return saveConfig();
}

View File

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

View File

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

View File

@@ -1,52 +0,0 @@
package ca.spottedleaf.moonrise.common.util;
import net.minecraft.world.level.levelgen.LegacyRandomSource;
/**
* Avoid costly CAS of superclass
*/
public final class SimpleRandom extends LegacyRandomSource {
private static final long MULTIPLIER = 25214903917L;
private static final long ADDEND = 11L;
private static final int BITS = 48;
private static final long MASK = (1L << BITS) - 1;
private long value;
public SimpleRandom(final long seed) {
super(0L);
this.value = seed;
}
@Override
public void setSeed(final long seed) {
this.value = (seed ^ MULTIPLIER) & MASK;
}
private long advanceSeed() {
return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
}
@Override
public int next(final int bits) {
return (int)(this.advanceSeed() >>> (BITS - bits));
}
@Override
public int nextInt() {
final long seed = this.advanceSeed();
return (int)(seed >>> (BITS - Integer.SIZE));
}
@Override
public int nextInt(final int bound) {
if (bound <= 0) {
throw new IllegalArgumentException();
}
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
final long value = this.advanceSeed() >>> (BITS - Integer.SIZE);
return (int)((value * (long)bound) >>> Integer.SIZE);
}
}

View File

@@ -0,0 +1,105 @@
package ca.spottedleaf.moonrise.common.util;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.BitRandomSource;
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian;
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
/**
* Avoid costly CAS of superclass + division in nextInt
*/
public final class SimpleThreadUnsafeRandom implements BitRandomSource {
private static final long MULTIPLIER = 25214903917L;
private static final long ADDEND = 11L;
private static final int BITS = 48;
private static final long MASK = (1L << BITS) - 1L;
private long value;
private final MarsagliaPolarGaussian gaussianSource = new MarsagliaPolarGaussian(this);
public SimpleThreadUnsafeRandom(final long seed) {
this.setSeed(seed);
}
@Override
public void setSeed(final long seed) {
this.value = (seed ^ MULTIPLIER) & MASK;
this.gaussianSource.reset();
}
private long advanceSeed() {
return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
}
@Override
public int next(final int bits) {
return (int)(this.advanceSeed() >>> (BITS - bits));
}
@Override
public int nextInt() {
final long seed = this.advanceSeed();
return (int)(seed >>> (BITS - Integer.SIZE));
}
@Override
public int nextInt(final int bound) {
if (bound <= 0) {
throw new IllegalArgumentException();
}
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
final long value = this.advanceSeed() >>> (BITS - Integer.SIZE);
return (int)((value * (long)bound) >>> Integer.SIZE);
}
@Override
public double nextGaussian() {
return this.gaussianSource.nextGaussian();
}
@Override
public RandomSource fork() {
return new SimpleThreadUnsafeRandom(this.nextLong());
}
@Override
public PositionalRandomFactory forkPositional() {
return new SimpleRandomPositionalFactory(this.nextLong());
}
public static final class SimpleRandomPositionalFactory implements PositionalRandomFactory {
private final long seed;
public SimpleRandomPositionalFactory(final long seed) {
this.seed = seed;
}
public long getSeed() {
return this.seed;
}
@Override
public RandomSource fromHashOf(final String string) {
return new SimpleThreadUnsafeRandom((long)string.hashCode() ^ this.seed);
}
@Override
public RandomSource fromSeed(final long seed) {
return new SimpleThreadUnsafeRandom(seed);
}
@Override
public RandomSource at(final int x, final int y, final int z) {
return new SimpleThreadUnsafeRandom(Mth.getSeed(x, y, z) ^ this.seed);
}
@Override
public void parityConfigString(final StringBuilder stringBuilder) {
stringBuilder.append("SimpleRandomPositionalFactory{").append(this.seed).append('}');
}
}
}

View File

@@ -0,0 +1,94 @@
package ca.spottedleaf.moonrise.common.util;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.BitRandomSource;
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian;
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
/**
* Avoid costly CAS of superclass
*/
public final class ThreadUnsafeRandom implements BitRandomSource {
private static final long MULTIPLIER = 25214903917L;
private static final long ADDEND = 11L;
private static final int BITS = 48;
private static final long MASK = (1L << BITS) - 1L;
private long value;
private final MarsagliaPolarGaussian gaussianSource = new MarsagliaPolarGaussian(this);
public ThreadUnsafeRandom(final long seed) {
this.setSeed(seed);
}
@Override
public void setSeed(final long seed) {
this.value = (seed ^ MULTIPLIER) & MASK;
this.gaussianSource.reset();
}
private long advanceSeed() {
return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
}
@Override
public int next(final int bits) {
return (int)(this.advanceSeed() >>> (BITS - bits));
}
@Override
public int nextInt() {
final long seed = this.advanceSeed();
return (int)(seed >>> (BITS - Integer.SIZE));
}
@Override
public double nextGaussian() {
return this.gaussianSource.nextGaussian();
}
@Override
public RandomSource fork() {
return new ThreadUnsafeRandom(this.nextLong());
}
@Override
public PositionalRandomFactory forkPositional() {
return new ThreadUnsafeRandomPositionalFactory(this.nextLong());
}
public static final class ThreadUnsafeRandomPositionalFactory implements PositionalRandomFactory {
private final long seed;
public ThreadUnsafeRandomPositionalFactory(final long seed) {
this.seed = seed;
}
public long getSeed() {
return this.seed;
}
@Override
public RandomSource fromHashOf(final String string) {
return new ThreadUnsafeRandom((long)string.hashCode() ^ this.seed);
}
@Override
public RandomSource fromSeed(final long seed) {
return new ThreadUnsafeRandom(seed);
}
@Override
public RandomSource at(final int x, final int y, final int z) {
return new ThreadUnsafeRandom(Mth.getSeed(x, y, z) ^ this.seed);
}
@Override
public void parityConfigString(final StringBuilder stringBuilder) {
stringBuilder.append("ThreadUnsafeRandomPositionalFactory{").append(this.seed).append('}');
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -43,4 +43,17 @@ abstract class PlayerListMixin {
)
private void doNotAdjustVD(final PlayerList instance, final Packet<?> packet) {}
/**
* @reason The RegionizedPlayerChunkLoader will handle the SD packet
* @author Spottedleaf
*/
@Redirect(
method = "setSimulationDistance",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/players/PlayerList;broadcastAll(Lnet/minecraft/network/protocol/Packet;)V"
)
)
private void doNotAdjustSD(final PlayerList instance, final Packet<?> packet) {}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,30 +1,26 @@
package ca.spottedleaf.moonrise.mixin.chunk_tick_iteration;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.SimpleRandom;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import ca.spottedleaf.moonrise.common.util.SimpleThreadUnsafeRandom;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer;
import net.minecraft.Util;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@Mixin(ServerChunkCache.class)
abstract class ServerChunkCacheMixin extends ChunkSource {
@@ -39,63 +35,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
@Unique
private final SimpleRandom shuffleRandom = new SimpleRandom(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);
}
}
private final SimpleThreadUnsafeRandom shuffleRandom = new SimpleThreadUnsafeRandom(0L);
/**
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
@@ -103,7 +43,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks()V",
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;J)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
@@ -113,4 +53,41 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
this.shuffleRandom.setSeed(randomSource.nextLong());
Util.shuffle(list, this.shuffleRandom);
}
/**
* @reason Do not iterate over entire chunk holder map; additionally perform mid-tick chunk task execution
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;J)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ChunkMap;forEachBlockTickingChunk(Ljava/util/function/Consumer;)V"
)
)
private void iterateTickingChunksFaster(final ChunkMap instance, final Consumer<LevelChunk> consumer) {
final ServerLevel world = this.level;
final int randomTickSpeed = world.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
// TODO check on update: impl of forEachBlockTickingChunk will only iterate ENTITY ticking chunks!
// TODO check on update: consumer just runs tickChunk
final ReferenceList<LevelChunk> entityTickingChunks = ((ChunkSystemServerLevel)world).moonrise$getEntityTickingChunks();
// note: we can use the backing array here because:
// 1. we do not care about new additions
// 2. _removes_ are impossible at this stage in the tick
final LevelChunk[] raw = entityTickingChunks.getRawDataUnchecked();
final int size = entityTickingChunks.size();
Objects.checkFromToIndex(0, size, raw.length);
for (int i = 0; i < size; ++i) {
world.tickChunk(raw[i], randomTickSpeed);
// call mid-tick tasks for chunk system
if ((i & 7) == 0) {
((ChunkSystemMinecraftServer)this.level.getServer()).moonrise$executeMidTickTasks();
continue;
}
}
}
}

View File

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

View File

@@ -5,16 +5,12 @@ import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
import com.mojang.serialization.MapCodec;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.spongepowered.asm.mixin.Mixin;
@@ -32,7 +28,7 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
protected BlockBehaviour.BlockStateBase.Cache cache;
@Shadow
public abstract VoxelShape getCollisionShape(BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext);
public abstract boolean isAir();
@Shadow
public VoxelShape[] occlusionShapesByFace;
@@ -99,10 +95,9 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
private void initCollisionState(final CallbackInfo ci) {
if (this.cache != null) {
final VoxelShape collisionShape = this.cache.collisionShape;
try {
this.constantCollisionShape = this.getCollisionShape(null, null, null);
} catch (final Throwable throwable) {
// :(
if (this.isAir()) {
this.constantCollisionShape = Shapes.empty();
} else {
this.constantCollisionShape = null;
}
this.occludesFullBlock = ((CollisionVoxelShape)collisionShape).moonrise$occludesFullBlock();

View File

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

View File

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

View File

@@ -439,7 +439,10 @@ abstract class ServerExplosionMixin {
* @author Spottedleaf
*/
@Redirect(
method = "hurtEntities",
method = {
"hurtEntities()V",
"hurtEntities(Ljava/util/List;)V" // Neo moves logic into this new method
},
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/ServerExplosion;getSeenPercent(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F"

View File

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

View File

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

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