Compare commits

..

1 Commits

Author SHA1 Message Date
Jason Penilla
a523823513 Add ticket on player join
https://github.com/PaperMC/Paper/pull/11398
2024-09-13 16:00:06 -07:00
215 changed files with 4411 additions and 6035 deletions

View File

@@ -1,53 +0,0 @@
name: Mod Incompatibility
description: Report an incompatibility with another mod.
title: '[Incompatibility: <NeoForge/Fabric/NeoForge & Fabric>]: Mod Name'
labels:
- mod-incompatibility
- needs-triage
body:
- type: input
id: mc_ver
attributes:
label: Minecraft Version
placeholder: e.g. 1.21.1 or 1.21.2
validations:
required: true
- type: input
id: moonrise_ver
attributes:
label: Moonrise Version
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
validations:
required: true
- type: dropdown
id: mod_loader
attributes:
label: Mod Loader
description: >-
The mod loader(s) this conflict happens with. When selecting 'Both' it
is expected you will submit logs for both as well. Be sure to update the
issue title.
options:
- NeoForge
- Fabric
- Both (NeoForge and Fabric)
validations:
required: true
- type: textarea
id: logs_crashes
attributes:
label: Logs and Crash Reports
description: >-
Include links to all relevant logs and crash reports (prefer using
https://gist.github.com/ or https://mclo.gs/). In case this is not
applicable fill in 'N/A' (it is expected you will provide context
below).
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional Context
description: Provide any relevant context, including version of the conflicting mod.
validations:
required: true

View File

@@ -1,55 +0,0 @@
name: Bug Report
description: Report a bug or Vanilla behavior parity issue.
title: '[Bug: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
labels:
- bug
- needs-triage
body:
- type: input
id: mc_ver
attributes:
label: Minecraft Version
placeholder: e.g. 1.21.1 or 1.21.2
validations:
required: true
- type: input
id: moonrise_ver
attributes:
label: Moonrise Version
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
validations:
required: true
- type: dropdown
id: mod_loader
attributes:
label: Mod Loader
description: >-
The mod loader(s) this bug happens with. When selecting 'Both' it
is expected you will submit logs for both as well. Be sure to update the
issue title.
options:
- NeoForge
- Fabric
- Both (NeoForge and Fabric)
validations:
required: true
- type: textarea
id: logs_crashes
attributes:
label: Logs and Crash Reports
description: >-
Include links to all relevant logs and crash reports (prefer using
https://gist.github.com/ or https://mclo.gs/). In case this is not
applicable fill in 'N/A' (it is expected you will provide context
below).
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional Context
description: >-
Provide any relevant context, including expected vs. actual behavior,
and reproduction steps.
validations:
required: true

View File

@@ -1,65 +0,0 @@
name: Performance Problem
description: Report a performance problem that doesn't fall under the bug category.
title: '[Performance: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
labels:
- performance
- needs-triage
body:
- type: input
id: mc_ver
attributes:
label: Minecraft Version
placeholder: e.g. 1.21.1, 1.21.2
validations:
required: true
- type: input
id: moonrise_ver
attributes:
label: Moonrise Version
placeholder: e.g. 0.1.0-beta.6 or Git commit hash
validations:
required: true
- type: dropdown
id: mod_loader
attributes:
label: Mod Loader
description: >-
The mod loader(s) this bug happens with. When selecting 'Both' it is
expected you have tested both as well. Be sure to update the issue
title.
options:
- NeoForge
- Fabric
- Both (NeoForge and Fabric)
validations:
required: true
- type: textarea
id: logs_crashes
attributes:
label: Logs and Crash Reports
description: >-
Include links to all relevant logs and crash reports (prefer using
https://gist.github.com/ or https://mclo.gs/). In case this is not
applicable fill in 'N/A' (it is expected you will provide context
below).
validations:
required: true
- type: textarea
id: profiler
attributes:
label: Profiler Data
description: >-
Provide profiler data showing the problem, from Spark or other tools. In
case this is not applicable fill in 'N/A' (it is expected you will
provide context below).
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional Context
description: >-
Provide any relevant context, including reproduction steps, world data,
videos, etc.
validations:
required: true

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Discord
url: https://discord.gg/tuinity
about: If you are unsure about something or have general questions, ask in our Discord before opening an issue.

View File

@@ -1,34 +0,0 @@
name: Feature Request
description: Request a new feature.
title: '[Feature Request: <NeoForge/Fabric/NeoForge & Fabric>]: Short description'
labels:
- enhancement
- needs-triage
body:
- type: input
id: mc_ver
attributes:
label: Minecraft Version
placeholder: e.g. 1.21.1, 1.21.2
validations:
required: true
- type: dropdown
id: mod_loader
attributes:
label: Mod Loader
description: The mod loader(s) this feature request is relevant for.
options:
- NeoForge
- Fabric
- Both (NeoForge and Fabric)
validations:
required: true
- type: textarea
id: context
attributes:
label: Description
description: >-
Describe the feature request in detail, including alternatives
considered.
validations:
required: true

View File

@@ -31,6 +31,8 @@ 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,6 +116,3 @@ run/
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
ConcurrentUtil/
YamlConfig/

3
.gitmodules vendored
View File

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

1
ConcurrentUtil Submodule

Submodule ConcurrentUtil added at 08d3ca3241

View File

@@ -1,10 +1,5 @@
Moonrise
==
[![Modrinth](https://img.shields.io/badge/Modrinth-gray?logo=modrinth)](https://modrinth.com/mod/moonrise-opt)
[![CurseForge](https://img.shields.io/badge/CurseForge-gray?logo=curseforge)](https://www.curseforge.com/minecraft/mc-mods/moonrise)
[![Release](https://img.shields.io/github/v/release/Tuinity/Moonrise?include_prereleases)](https://github.com/Tuinity/Moonrise/releases)
[![License](https://img.shields.io/github/license/Tuinity/Moonrise)](LICENSE.md)
Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/LAN) and dedicated server.
@@ -12,20 +7,20 @@ Fabric/NeoForge mod for optimising performance of the integrated (singleplayer/L
Moonrise aims to optimise the game *without changing Vanilla behavior*. If you find that there are changes to Vanilla behavior,
please open an issue.
Moonrise is an official port of several important [Paper](https://github.com/PaperMC/Paper/)
Moonrise ports several important [Paper](https://github.com/PaperMC/Paper/)
patches. Listed below are notable patches:
- [Starlight](https://github.com/PaperMC/Starlight/)
- Chunk system rewrite
- Collision optimisations
- Entity tracker optimisations
- Random ticking optimisations
- [Starlight](https://github.com/PaperMC/Starlight/)
## Known Compatibility Issues
| 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>✅ 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> |
| FerriteCore | <details><summary>📝 requires config changes</summary>In `config/ferritecore-mixin.toml`:<br/>Set `replaceNeighborLookup` and `replacePropertyMap` to `false`</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
Moonrise provides documented configuration options for tuning the chunk system and enabling bugfixes in the config file `$mcdir$/config/moonrise.yml`.

View File

@@ -1,53 +1,40 @@
import me.modmuss50.mpp.ReleaseType
plugins {
id("java-library")
id("net.neoforged.moddev")
id("me.modmuss50.mod-publish-plugin") version "0.8.4" apply false
id("xyz.jpenilla.quiet-architectury-loom")
id("me.modmuss50.mod-publish-plugin") version "0.7.2" apply false
}
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() })
/*
* 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()
}
runConfigCommon {
systemProperties.put "mixin.debug", "true"
systemProperties.put "Moonrise.MaxViewDistance", "128"
}
dependencies {
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)
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
// todo: does cloth publish a platform-agnostic jar in mojang mappings?
compileOnly "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
}
File awFile = file("src/main/resources/moonrise.accesswidener")
allprojects {
group = rootProject.maven_group
version = rootProject.mod_version + "+" + getGitCommit.get()
version = rootProject.mod_version + "+" + getGitCommit()
plugins.apply("java-library")
plugins.apply("xyz.jpenilla.quiet-architectury-loom")
java {
withSourcesJar()
@@ -57,30 +44,25 @@ allprojects {
}
}
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter:${rootProject.junit_version}"
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.test {
useJUnitPlatform()
}
repositories {
maven {
url = "https://repo.papermc.io/repository/maven-public/"
mavenLocal {
mavenContent {
includeGroup("ca.spottedleaf")
includeModule("ca.spottedleaf", "concurrentutil")
}
}
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/" }
maven { url "https://maven.shedaniel.me/" }
maven { url "https://maven.terraformersmc.com/releases/" }
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings loom.officialMojangMappings()
}
// make build reproducible
@@ -98,22 +80,31 @@ allprojects {
rename { "${it}_${rootProject.base.archivesName.get()}"}
}
}
loom {
accessWidenerPath = awFile
mixin {
useLegacyMixinAp = false
}
}
}
subprojects {
plugins.apply("me.modmuss50.mod-publish-plugin")
plugins.apply 'java-library'
plugins.apply 'com.gradleup.shadow'
loom.mods {
main {
sourceSet("main")
sourceSet("main", project.rootProject)
}
}
loom.runs.all {
ideConfigGenerated true
// property "mixin.debug", "true"
}
configurations.create("libs")
configurations.shadow {
extendsFrom(configurations.libs)
}
configurations.implementation {
extendsFrom(configurations.libs)
}
plugins.apply("me.modmuss50.mod-publish-plugin")
publishMods {
file = remapJar.archiveFile
if (project.version.contains("-beta.")) {
type = ReleaseType.BETA
} else {
@@ -121,30 +112,20 @@ subprojects {
}
changelog = providers.environmentVariable("RELEASE_NOTES")
List<String> supportedMcVersions = rootProject.supported_minecraft_versions.split(',')
modrinth {
projectId = "KOHu7RCS"
accessToken = providers.environmentVariable("MODRINTH_TOKEN")
minecraftVersions = supportedMcVersions
minecraftVersions = [rootProject.minecraft_version]
}
curseforge {
projectId = "1096335"
accessToken = providers.environmentVariable("CURSEFORGE_TOKEN")
minecraftVersions = supportedMcVersions
minecraftVersions = [rootProject.minecraft_version]
}
}
// Setup a run with lithium for compatibility testing
configurations.create("lithium")
dependencies {
String coordinates = "maven.modrinth:lithium:"
if (getProject().name == "Moonrise-NeoForge") {
coordinates += rootProject.neo_lithium_version
} else {
coordinates += rootProject.fabric_lithium_version
}
lithium coordinates
}
}
loom.runs.all {
ideConfigGenerated false
}

View File

@@ -1,11 +0,0 @@
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

@@ -1,152 +0,0 @@
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

@@ -1,44 +0,0 @@
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

@@ -1,24 +0,0 @@
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

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

View File

@@ -1,60 +1,51 @@
import java.util.stream.Collectors
import net.fabricmc.loom.util.gradle.SourceSetHelper
plugins {
id("quiet-fabric-loom")
id("xyz.jpenilla.quiet-architectury-loom")
id 'maven-publish'
id 'com.gradleup.shadow'
}
configurations.create("libs")
configurations.shadow {
extendsFrom(configurations.libs)
}
configurations.implementation {
extendsFrom(configurations.libs)
}
dependencies {
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}"
add('shadow', project([path: ":", configuration: "namedElements"]))
runtimeOnly(project(":").sourceSets.main.output)
shadow(project(":"))
compileOnly(project(":"))
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
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 "com.terraformersmc:modmenu:11.0.1"
modImplementation platform(fabricApiLibs.bom)
modImplementation fabricApiLibs.command.api.v2
modImplementation fabricApiLibs.lifecycle.events.v1
include fabricApiLibs.command.api.v2
include fabricApiLibs.base
}
tasks.processResources {
def properties = [
"version": project.version,
"minecraft_version": minecraft_version,
"loader_version": loader_version,
"mod_version": mod_version
]
inputs.properties(properties)
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand properties
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
}
}
tasks.shadowJar {
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'
}
publishMods {
file = remapJar.archiveFile
modLoaders = ["fabric"]
modrinth {
@@ -68,56 +59,26 @@ publishMods {
incompatible(
"not-enough-crashes",
"starlight",
"c2me"
"c2me-fabric"
)
}
}
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")
configurations.create("lithium")
loom {
createRemapConfigurations(sourceSets.lithium)
runs {
register("lithiumClient") {
client()
property "mixin.debug", "true"
}
}
}
configurations.modLithiumRuntimeOnly {
extendsFrom configurations.lithium
}
tasks.named("runLithiumClient", net.fabricmc.loom.task.RunGameTask.class) {
getClasspath().from(configurations.modRuntimeClasspathLithiumMapped)
}
dependencies {
modLithiumRuntimeOnly "maven.modrinth:lithium:${rootProject.lithium_version}"
}

View File

@@ -1,44 +1,29 @@
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 ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
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.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB;
import java.util.Collection;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.function.Predicate;
public final class FabricHooks extends BaseChunkSystemHooks implements PlatformHooks {
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
public final class FabricHooks implements PlatformHooks {
@Override
public String getBrand() {
@@ -57,6 +42,16 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
};
}
@Override
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
}
@Override
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
return original;
}
@Override
public boolean hasCurrentlyLoadingChunk() {
return false;
@@ -74,12 +69,7 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
@Override
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel)newChunk.getLevel(), newChunk);
if (!(original instanceof ImposterProtoChunk)) {
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
}
}
}
@Override
@@ -88,19 +78,17 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
}
@Override
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
}
@Override
public void chunkUnloadFromWorld(final LevelChunk chunk) {
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel)chunk.getLevel(), chunk);
}
}
@Override
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
}
@@ -117,43 +105,13 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
@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
@@ -202,12 +160,12 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
}
@Override
public long configAutoSaveInterval(final ServerLevel world) {
public long configAutoSaveInterval() {
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
}
@Override
public int configMaxAutoSavePerTick(final ServerLevel world) {
public int configMaxAutoSavePerTick() {
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
}
@@ -215,52 +173,4 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
public boolean configFixMC159283() {
return ConfigHolder.getConfig().bugFixes.fixMC159283;
}
@Override
public boolean forceNoSave(final ChunkAccess chunk) {
return false;
}
@Override
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
final int fromVersion, final int toVersion) {
return (CompoundTag)dataFixer.update(
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
).getValue();
}
@Override
public boolean hasMainChunkLoadHook() {
return false;
}
@Override
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
}
@Override
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
return entities;
}
@Override
public void unloadEntity(final Entity entity) {
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
}
@Override
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
}
@Override
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
@Override
public long[] getCounterTypesUncached(final TicketType type) {
return type == TicketType.FORCED ? new long[] { ChunkSystemTicketType.COUNTER_TYPE_FORCED } : LongArrays.EMPTY_ARRAY;
}
}

View File

@@ -0,0 +1,33 @@
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,183 +0,0 @@
package ca.spottedleaf.moonrise.fabric.mixin.collisions;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.TagKey;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(Entity.class)
abstract class EntityMixin {
@Shadow
public abstract boolean touchingUnloadedChunk();
@Shadow
public abstract AABB getBoundingBox();
@Shadow
@Deprecated
public abstract boolean isPushedByFluid();
@Shadow
private Level level;
@Shadow
@Deprecated
protected Object2DoubleMap<TagKey<Fluid>> fluidHeight;
@Shadow
public abstract Vec3 getDeltaMovement();
@Shadow
public abstract void setDeltaMovement(final Vec3 arg);
/**
* @reason Optimise the block reading in this function
* @author Spottedleaf
*/
@Overwrite
public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, final double flowScale) {
if (this.touchingUnloadedChunk()) {
return false;
}
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
final Level world = this.level;
final int minSection = WorldUtil.getMinSection(world);
final int minBlockX = Mth.floor(boundingBox.minX);
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
final int minBlockZ = Mth.floor(boundingBox.minZ);
// note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
final boolean isPushable = this.isPushedByFluid();
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
Vec3 pushVector = Vec3.ZERO;
double totalPushes = 0.0;
double maxHeightDiff = 0.0;
boolean inFluid = false;
final int minChunkX = minBlockX >> 4;
final int maxChunkX = maxBlockX >> 4;
final int minChunkY = minBlockY >> 4;
final int maxChunkY = maxBlockY >> 4;
final int minChunkZ = minBlockZ >> 4;
final int maxChunkZ = maxBlockZ >> 4;
final ChunkSource chunkSource = world.getChunkSource();
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
// bound y
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
if (sectionIdx < 0 || sectionIdx >= sections.length) {
continue;
}
final LevelChunkSection section = sections[sectionIdx];
if (section.hasOnlyAir()) {
// empty
continue;
}
final PalettedContainer<BlockState> blocks = section.states;
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
if (fluidState.isEmpty() || !fluidState.is(fluid)) {
continue;
}
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
final double diff = height - boundingBox.minY;
if (diff < 0.0) {
continue;
}
inFluid = true;
maxHeightDiff = Math.max(maxHeightDiff, diff);
if (!isPushable) {
continue;
}
++totalPushes;
final Vec3 flow = fluidState.getFlow(world, mutablePos);
if (diff < 0.4) {
pushVector = pushVector.add(flow.scale(diff));
} else {
pushVector = pushVector.add(flow);
}
}
}
}
}
}
}
this.fluidHeight.put(fluid, maxHeightDiff);
if (pushVector.lengthSqr() == 0.0) {
return inFluid;
}
// note: totalPushes != 0 as pushVector != 0
pushVector = pushVector.scale(1.0 / totalPushes);
final Vec3 currMovement = this.getDeltaMovement();
if (!((Entity)(Object)this instanceof Player)) {
pushVector = pushVector.normalize();
}
pushVector = pushVector.scale(flowScale);
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
pushVector = pushVector.normalize().scale(0.0045000000000000005);
}
this.setDeltaMovement(currMovement.add(pushVector));
// note: inFluid = true here as pushVector != 0
return true;
}
}

View File

@@ -1,76 +1,71 @@
{
"schemaVersion": 1,
"id": "moonrise",
"version": "${version}",
"name": "Moonrise",
"description": "Optimisation mod for the dedicated and integrated server.",
"authors": [
"Spottedleaf"
"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"
],
"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.4 <1.21.6",
"fabric-command-api-v2": "*"
},
"custom": {
"lithium:options": {
"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
},
"ferritecore:disabled_options": [
"replaceNeighborLookup",
"replacePropertyMap"
]
"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.21.1",
"fabric-command-api-v2": "*"
},
"custom": {
"lithium:options": {
"mixin.collections.chunk_tickets": false,
"mixin.world.temperature_cache": false,
"mixin.block.flatten_states": false,
"mixin.math.fast_util": false,
"mixin.minimal_nonvanilla.collisions.empty_space": false,
"mixin.alloc.deep_passengers": false,
"mixin.math.fast_blockpos": false,
"mixin.shapes.blockstate_cache": false,
"mixin.world.block_entity_ticking": false,
"mixin.collections.entity_ticking": false,
"mixin.world.chunk_access": false,
"mixin.ai.poi": false,
"mixin.chunk.no_validation": false,
"mixin.minimal_nonvanilla.world.expiring_chunk_tickets": false,
"mixin.world.tick_scheduler": false,
"mixin.alloc.chunk_ticking": false,
"mixin.entity.replace_entitytype_predicates": false,
"mixin.util.block_tracking": false,
"mixin.shapes.specialized_shapes": false,
"mixin.shapes.optimized_matching": false,
"mixin.entity.collisions.intersection": false,
"mixin.entity.collisions.movement": false,
"mixin.entity.collisions.unpushable_cramming": false,
"mixin.chunk.entity_class_groups": false,
"mixin.alloc.entity_tracker": false
}
}
}

View File

@@ -2,9 +2,9 @@
"parent": "moonrise.mixins.json",
"package": "ca.spottedleaf.moonrise.fabric.mixin",
"mixins": [
"chunk_system.FabricDistanceManagerMixin",
"chunk_system.FabricMinecraftServerMixin",
"chunk_system.FabricServerLevelMixin",
"collisions.EntityMixin"
"chunk_system.FabricServerLevelMixin"
],
"client": [
"command.ClientSuggestionProviderMixin"

View File

@@ -1,20 +0,0 @@
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,26 +1,16 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx2G
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.daemon=false
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
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=nhc57Td2
neo_lithium_version=P5VT33Jo
minecraft_version=1.21.1
loader_version=0.16.5
neoforge_version=21.1.42
snakeyaml_version=2.2
concurrentutil_version=0.0.2-SNAPSHOT
cloth_version=15.0.128
lithium_version=mc1.21.1-0.13.0
# Mod Properties
mod_version=0.3.0-beta.2
mod_version=0.1.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.12-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

3
gradlew vendored
View File

@@ -86,7 +86,8 @@ 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\n' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

6
installConcurrentUtil.sh Executable file
View File

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

View File

@@ -1,10 +1,9 @@
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import net.neoforged.moddevgradle.internal.RunGameTask
import net.fabricmc.loom.util.aw2at.Aw2At
plugins {
id("net.neoforged.moddev")
id("xyz.jpenilla.quiet-architectury-loom")
id 'maven-publish'
id 'com.gradleup.shadow'
}
repositories {
@@ -14,141 +13,57 @@ repositories {
}
}
def aw2at = Aw2AtTask.configureDefault(
getProject(),
rootProject.layout.projectDirectory.file("src/main/resources/moonrise.accesswidener").getAsFile(),
sourceSets.main
)
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
}
configurations.implementation {
extendsFrom(configurations.shadow)
}
dependencies {
runtimeOnly(project(":").sourceSets.main.output)
shadow(project(":"))
compileOnly(project(":"))
add('shadow', project([path: ":", configuration: "namedElements"]))
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
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}")
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
forgeExtra("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}"
modImplementation "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
}
tasks.processResources {
def properties = [
"version": project.version,
"minecraft_version": minecraft_version,
"mod_version": mod_version
]
inputs.properties(properties)
processResources {
inputs.property "version", project.version
filesMatching("META-INF/neoforge.mods.toml") {
expand properties
expand "version": project.version, "minecraft_version": minecraft_version, "loader_version": loader_version, "mod_version": mod_version
}
}
tasks.jar {
archiveClassifier = "dev"
}
tasks.shadowJar {
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'
}
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
}
Aw2At.setup(getProject(), tasks.remapJar)
publishMods {
file = productionJar.archiveFile
modLoaders = ["neoforge"]
modrinth {
incompatible(
"notenoughcrashes",
"starlight-neoforge",
"canary"
"canary",
"radium"
)
}
curseforge {
incompatible(
"not-enough-crashes-forge",
"starlight-neoforge",
"canary"
"canary",
"radium-reforged"
)
}
}
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

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

View File

@@ -1,36 +1,28 @@
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 ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
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;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.entity.PartEntity;
@@ -38,11 +30,10 @@ 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 extends BaseChunkSystemHooks implements PlatformHooks {
public final class NeoForgeHooks implements PlatformHooks {
@Override
public String getBrand() {
@@ -61,6 +52,16 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
};
}
@Override
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
EventHooks.onExplosionDetonate(world, explosion, possiblyAffecting, diameter);
}
@Override
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
return EventHooks.getExplosionKnockback(world, explosion, entity, original);
}
@Override
public boolean hasCurrentlyLoadingChunk() {
return true;
@@ -87,12 +88,10 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
}
@Override
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
final ChunkPos pos = holder.getPos();
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
EventHooks.fireChunkTicketLevelUpdated(
world, CoordinateUtils.getChunkKey(pos.x, pos.z),
oldLevel, newLevel, holder
world, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ),
oldLevel, newLevel, holder.vanillaChunkHolder
);
}
@@ -102,7 +101,7 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
}
@Override
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
}
@@ -119,12 +118,7 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
@Override
public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
final List<Entity> into) {
final Collection<PartEntity<?>> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final PartEntity<?> part : parts) {
for (final PartEntity<?> part : world.getPartEntities()) {
if (part != entity && part.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(part))) {
into.add(part);
}
@@ -139,18 +133,9 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
return;
}
final Collection<PartEntity<?>> parts = world.dragonParts();
if (parts.isEmpty()) {
return;
}
for (final PartEntity<?> part : parts) {
if (!part.getBoundingBox().intersects(boundingBox)) {
continue;
}
for (final PartEntity<?> part : world.getPartEntities()) {
final T casted = (T)entityTypeTest.tryCast(part);
if (casted != null && (predicate == null || predicate.test(casted))) {
if (casted != null && casted.getBoundingBox().intersects(boundingBox) && (predicate == null || predicate.test(casted))) {
into.add(casted);
if (into.size() >= maxCount) {
break;
@@ -208,12 +193,12 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
}
@Override
public long configAutoSaveInterval(final ServerLevel world) {
public long configAutoSaveInterval() {
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
}
@Override
public int configMaxAutoSavePerTick(final ServerLevel world) {
public int configMaxAutoSavePerTick() {
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
}
@@ -221,61 +206,4 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
public boolean configFixMC159283() {
return ConfigHolder.getConfig().bugFixes.fixMC159283;
}
@Override
public boolean forceNoSave(final ChunkAccess chunk) {
return false;
}
@Override
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
final int fromVersion, final int toVersion) {
return (CompoundTag)dataFixer.update(
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
).getValue();
}
@Override
public boolean hasMainChunkLoadHook() {
return true;
}
@Override
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, chunkData));
}
@Override
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
return entities;
}
@Override
public void unloadEntity(final Entity entity) {
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
}
@Override
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
}
@Override
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
@Override
public long[] getCounterTypesUncached(final TicketType type) {
final LongArrayList ret = new LongArrayList();
if (type == TicketType.FORCED) {
ret.add(ChunkSystemTicketType.COUNTER_TYPE_FORCED);
}
if (type.forceNaturalSpawning()) {
ret.add(ChunkSystemTicketType.COUNTER_TYPER_NATURAL_SPAWNING_FORCED);
}
return ret.toLongArray();
}
}

View File

@@ -0,0 +1,79 @@
package ca.spottedleaf.moonrise.neoforge.mixin.chunk_system;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager;
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
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;
import org.spongepowered.asm.mixin.Unique;
@Mixin(DistanceManager.class)
abstract class NeoForgeDistanceManagerMixin implements ChunkSystemDistanceManager {
@Unique
private final ConcurrentLong2ReferenceChainedHashTable<SortedArraySet<Ticket<?>>> mtSafeForcedTickets = new ConcurrentLong2ReferenceChainedHashTable<>();
/**
* @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.mtSafeForcedTickets.compute(pos.toLong(), (final long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
final SortedArraySet<Ticket<?>> ret;
if (valueInMap != null) {
ret = valueInMap;
} else {
ret = SortedArraySet.create(4);
}
ret.add(forceTicket);
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.mtSafeForcedTickets.computeIfPresent(pos.toLong(), (final long keyInMap, final SortedArraySet<Ticket<?>> valueInMap) -> {
valueInMap.remove(forceTicket);
return valueInMap.isEmpty() ? null : valueInMap;
});
}
}
/**
* @reason Make this API thread-safe
* @author Spottedleaf
*/
@Overwrite
public boolean shouldForceTicks(final long chunkPos) {
return this.mtSafeForcedTickets.containsKey(chunkPos);
}
}

View File

@@ -1,69 +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.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,199 +0,0 @@
package ca.spottedleaf.moonrise.neoforge.mixin.collisions;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.neoforge.patches.collisions.FluidPushCalculation;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.extensions.IEntityExtension;
import net.neoforged.neoforge.fluids.FluidType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Iterator;
@Mixin(Entity.class)
abstract class EntityMixin implements IEntityExtension {
@Shadow
public abstract boolean touchingUnloadedChunk();
@Shadow
public abstract AABB getBoundingBox();
@Shadow
private Level level;
@Shadow
protected abstract void setFluidTypeHeight(final FluidType type, final double height);
@Shadow
public abstract Vec3 getDeltaMovement();
@Shadow
public abstract void setDeltaMovement(final Vec3 arg);
/**
* @reason Optimise the block reading in this function
* @author Spottedleaf
*/
@Overwrite
public void updateFluidHeightAndDoFluidPushing() {
if (this.touchingUnloadedChunk()) {
return;
}
final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
final Level world = this.level;
final int minSection = WorldUtil.getMinSection(world);
final int minBlockX = Mth.floor(boundingBox.minX);
final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
final int minBlockZ = Mth.floor(boundingBox.minZ);
// note: bounds are exclusive in Vanilla, so we subtract 1
final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
final int minChunkX = minBlockX >> 4;
final int maxChunkX = maxBlockX >> 4;
final int minChunkY = minBlockY >> 4;
final int maxChunkY = maxBlockY >> 4;
final int minChunkZ = minBlockZ >> 4;
final int maxChunkZ = maxBlockZ >> 4;
final ChunkSource chunkSource = world.getChunkSource();
final Reference2ReferenceArrayMap<FluidType, FluidPushCalculation> calculations = new Reference2ReferenceArrayMap<>();
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
// bound y
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
if (sectionIdx < 0 || sectionIdx >= sections.length) {
continue;
}
final LevelChunkSection section = sections[sectionIdx];
if (section.hasOnlyAir()) {
// empty
continue;
}
final PalettedContainer<BlockState> blocks = section.states;
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
if (fluidState.isEmpty()) {
continue;
}
mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
final FluidType type = fluidState.getFluidType();
// note: assume fluidState.isEmpty() == type.isAir()
final FluidPushCalculation calculation = calculations.computeIfAbsent(type, (final FluidType key) -> {
return new FluidPushCalculation();
});
final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
final double diff = height - boundingBox.minY;
if (diff < 0.0) {
continue;
}
calculation.maxHeightDiff = Math.max(calculation.maxHeightDiff, diff);
if (calculation.isPushed == Boolean.FALSE) {
continue;
} else if (calculation.isPushed == null) {
final boolean isPushed = this.isPushedByFluid(type);
calculation.isPushed = Boolean.valueOf(isPushed);
if (!isPushed) {
continue;
}
}
++calculation.totalPushes;
final Vec3 flow = fluidState.getFlow(world, mutablePos);
if (diff < 0.4) {
calculation.pushVector = calculation.pushVector.add(flow.scale(diff));
} else {
calculation.pushVector = calculation.pushVector.add(flow);
}
}
}
}
}
}
}
if (calculations.isEmpty()) {
return;
}
for (final Iterator<Reference2ReferenceMap.Entry<FluidType, FluidPushCalculation>> iterator = calculations.reference2ReferenceEntrySet().fastIterator(); iterator.hasNext();) {
final Reference2ReferenceMap.Entry<FluidType, FluidPushCalculation> entry = iterator.next();
final FluidType type = entry.getKey();
final FluidPushCalculation calculation = entry.getValue();
this.setFluidTypeHeight(type, calculation.maxHeightDiff);
Vec3 pushVector = calculation.pushVector;
if (pushVector.lengthSqr() == 0.0) {
continue;
}
// note: totalPushes != 0 as pushVector != 0
pushVector = pushVector.scale(1.0 / calculation.totalPushes);
final Vec3 currMovement = this.getDeltaMovement();
if (!((Entity)(Object)this instanceof Player)) {
pushVector = pushVector.normalize();
}
pushVector = pushVector.scale(this.getFluidMotionScale(type));
if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
pushVector = pushVector.normalize().scale(0.0045000000000000005);
}
this.setDeltaMovement(currMovement.add(pushVector));
}
}
}

View File

@@ -1,12 +0,0 @@
package ca.spottedleaf.moonrise.neoforge.patches.collisions;
import net.minecraft.world.phys.Vec3;
public final class FluidPushCalculation {
public Vec3 pushVector = Vec3.ZERO;
public double totalPushes = 0.0;
public double maxHeightDiff = 0.0;
public Boolean isPushed = null;
}

View File

@@ -13,10 +13,6 @@ displayURL = "https://github.com/Tuinity/Moonrise"
authors = "Spottedleaf"
description = "Optimisation mod for the dedicated and integrated server."
displayTest = "IGNORE_ALL_VERSION"
"ferritecore:disabled_options" = [
"replaceNeighborLookup",
"replacePropertyMap"
]
[[dependencies.moonrise]]
modId = "neoforge"
@@ -28,7 +24,7 @@ side = "BOTH"
[[dependencies.moonrise]]
modId = "minecraft"
type = "required"
versionRange = "(1.21.4,1.21.6)"
versionRange = "[1.21,1.21.2)"
ordering = "NONE"
side = "BOTH"
@@ -40,11 +36,20 @@ type = "incompatible"
modId = "starlight"
type = "incompatible"
[[dependencies.moonrise]]
modId = "lithium"
type = "incompatible"
[[dependencies.moonrise]]
# unofficial lithium port
modId = "canary"
type = "incompatible"
[[dependencies.moonrise]]
# unofficial lithium port
modId = "radium"
type = "incompatible"
[[dependencies.moonrise]]
modId = "c2me"
type = "incompatible"
@@ -54,31 +59,3 @@ config = "moonrise.mixins.json"
[[mixins]]
config = "moonrise-neoforge.mixins.json"
["lithium:options"]
"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

View File

@@ -2,10 +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"
"chunk_system.NeoForgeServerLevelMixin"
],
"client": [
"command.ClientCommandSourceStackMixin"

View File

@@ -1,17 +0,0 @@
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,6 +13,7 @@ pluginManagement {
maven {
name = 'jmp'
url = 'https://repo.jpenilla.xyz/snapshots'
mavenContent { snapshotsOnly() }
}
maven {
name = 'architectury'
@@ -22,10 +23,9 @@ pluginManagement {
}
plugins {
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
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.295" apply false
id 'com.gradleup.shadow' version '8.3.0' apply false
}
dependencyResolutionManagement {
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
}
versionCatalogs {
create("fabricApiLibs") {
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
from("net.fabricmc.fabric-api:fabric-api-catalog:0.103.0+1.21.1")
}
}
}
@@ -48,6 +48,3 @@ 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,30 +1,28 @@
package ca.spottedleaf.moonrise.common;
import ca.spottedleaf.moonrise.common.util.ChunkSystemHooks;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
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.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Predicate;
public interface PlatformHooks extends ChunkSystemHooks {
public interface PlatformHooks {
public static PlatformHooks get() {
return Holder.INSTANCE;
}
@@ -35,6 +33,10 @@ public interface PlatformHooks extends ChunkSystemHooks {
public Predicate<BlockState> maybeHasLightEmission();
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original);
public boolean hasCurrentlyLoadingChunk();
public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
@@ -45,11 +47,11 @@ public interface PlatformHooks extends ChunkSystemHooks {
public boolean allowAsyncTicketUpdates();
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel);
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
public void chunkUnloadFromWorld(final LevelChunk chunk);
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data);
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
@@ -64,6 +66,8 @@ public interface PlatformHooks extends ChunkSystemHooks {
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();
@@ -78,30 +82,12 @@ public interface PlatformHooks extends ChunkSystemHooks {
public int configPlayerMaxConcurrentGens();
public long configAutoSaveInterval(final ServerLevel world);
public long configAutoSaveInterval();
public int configMaxAutoSavePerTick(final ServerLevel world);
public int configMaxAutoSavePerTick();
public boolean configFixMC159283();
// support for CB chunk mustNotSave
public boolean forceNoSave(final ChunkAccess chunk);
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
final int fromVersion, final int toVersion);
public boolean hasMainChunkLoadHook();
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
public void unloadEntity(final Entity entity);
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk);
public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
public static final class Holder {
private Holder() {
}

View File

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

View File

@@ -0,0 +1,11 @@
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

@@ -0,0 +1,307 @@
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

@@ -0,0 +1,46 @@
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

@@ -0,0 +1,45 @@
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

@@ -0,0 +1,59 @@
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

@@ -0,0 +1,47 @@
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

@@ -0,0 +1,33 @@
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

@@ -0,0 +1,36 @@
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

@@ -0,0 +1,27 @@
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

@@ -0,0 +1,35 @@
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

@@ -0,0 +1,36 @@
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

@@ -0,0 +1,33 @@
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

@@ -0,0 +1,36 @@
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

@@ -0,0 +1,29 @@
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

@@ -0,0 +1,30 @@
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

@@ -0,0 +1,37 @@
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

@@ -0,0 +1,24 @@
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

@@ -0,0 +1,15 @@
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

@@ -0,0 +1,45 @@
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

@@ -0,0 +1,178 @@
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,21 +251,4 @@ 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

@@ -0,0 +1,38 @@
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

@@ -0,0 +1,22 @@
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

@@ -0,0 +1,76 @@
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

@@ -19,13 +19,6 @@ public final class IntList {
return this.count;
}
public void setMinCapacity(final int len) {
final int[] byIndex = this.byIndex;
if (byIndex.length < len) {
this.byIndex = Arrays.copyOf(byIndex, len);
}
}
public int getRaw(final int index) {
return this.byIndex[index];
}

View File

@@ -2,7 +2,6 @@ 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;
@@ -22,34 +21,15 @@ public final class IteratorSafeOrderedReferenceSet<E> {
private int iteratorCount;
public IteratorSafeOrderedReferenceSet() {
this(Object.class);
}
public IteratorSafeOrderedReferenceSet(final Class<? super E> arrComponent) {
this(16, 0.75f, 16, 0.2, arrComponent);
this(16, 0.75f, 16, 0.2);
}
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[])Array.newInstance(arrComponent, arrayCapacity);
}
// includes null (gravestone) elements
public E[] getListRaw() {
return this.listElements;
}
// includes null (gravestone) elements
public int getListSize() {
return this.listSize;
this.listElements = (E[])new Object[arrayCapacity];
}
/*
@@ -101,7 +81,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
public int createRawIterator() {
++this.iteratorCount;
if (this.indexMap.isEmpty()) {
return Integer.MAX_VALUE;
return -1;
} else {
return this.firstInvalidIndex == 0 ? this.indexMap.getInt(this.indexMap.firstKey()) : 0;
}
@@ -116,7 +96,7 @@ public final class IteratorSafeOrderedReferenceSet<E> {
}
}
return Integer.MAX_VALUE;
return -1;
}
public void finishRawIterator() {
@@ -225,6 +205,10 @@ 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,77 +0,0 @@
package ca.spottedleaf.moonrise.common.list;
import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap;
import java.util.Arrays;
public final class ShortList {
private final Short2ShortOpenHashMap map = new Short2ShortOpenHashMap();
{
this.map.defaultReturnValue(Short.MIN_VALUE);
}
private static final short[] EMPTY_LIST = new short[0];
private short[] byIndex = EMPTY_LIST;
private short count;
public int size() {
return (int)this.count;
}
public short getRaw(final int index) {
return this.byIndex[index];
}
public void setMinCapacity(final int len) {
final short[] byIndex = this.byIndex;
if (byIndex.length < len) {
this.byIndex = Arrays.copyOf(byIndex, len);
}
}
public boolean add(final short value) {
final int count = (int)this.count;
final short currIndex = this.map.putIfAbsent(value, (short)count);
if (currIndex != Short.MIN_VALUE) {
return false; // already in this list
}
short[] list = this.byIndex;
if (list.length == count) {
// resize required
list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
}
list[count] = value;
this.count = (short)(count + 1);
return true;
}
public boolean remove(final short value) {
final short index = this.map.remove(value);
if (index == Short.MIN_VALUE) {
return false;
}
// move the entry at the end to this index
final short endIndex = --this.count;
final short end = this.byIndex[endIndex];
if (index != endIndex) {
// not empty after this call
this.map.put(end, index);
}
this.byIndex[(int)index] = end;
this.byIndex[(int)endIndex] = (short)0;
return true;
}
public void clear() {
this.count = (short)0;
this.map.clear();
}
}

View File

@@ -1,20 +1,16 @@
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.patches.chunk_system.level.ChunkSystemLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import java.util.ArrayList;
public final class NearbyPlayers {
@@ -24,27 +20,7 @@ public final class NearbyPlayers {
GENERAL_REALLY_SMALL,
TICK_VIEW_DISTANCE,
VIEW_DISTANCE,
// Moonrise start - chunk tick iteration
SPAWN_RANGE {
@Override
void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
((ChunkTickServerLevel)world).moonrise$addPlayerTickingRequest(chunkX, chunkZ);
}
@Override
void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
((ChunkTickServerLevel)world).moonrise$removePlayerTickingRequest(chunkX, chunkZ);
}
};
// Moonrise end - chunk tick iteration
void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
}
void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
}
SPAWN_RANGE, // Moonrise - chunk tick iteration
}
private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values();
@@ -61,12 +37,6 @@ public final class NearbyPlayers {
private final ServerLevel world;
private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
{
for (int i = 0; i < this.directByChunk.length; ++i) {
this.directByChunk[i] = new Long2ReferenceOpenHashMap<>();
}
}
public NearbyPlayers(final ServerLevel world) {
this.world = world;
@@ -100,16 +70,6 @@ public final class NearbyPlayers {
}
}
public void clear() {
if (this.players.isEmpty()) {
return;
}
for (final ServerPlayer player : new ArrayList<>(this.players.keySet())) {
this.removePlayer(player);
}
}
public void tickPlayer(final ServerPlayer player) {
final TrackedPlayer[] players = this.players.get(player);
if (players == null) {
@@ -121,8 +81,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, PlatformHooks.get().getTickViewDistance(player));
players[NearbyMapType.VIEW_DISTANCE.ordinal()].update(chunk.x, chunk.z, PlatformHooks.get().getViewDistance(player));
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.SPAWN_RANGE.ordinal()].update(chunk.x, chunk.z, ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE); // Moonrise - chunk tick iteration
}
@@ -134,41 +94,38 @@ public final class NearbyPlayers {
return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
}
public TrackedChunk getChunk(final int chunkX, final int chunkZ) {
return this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
}
public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public ReferenceList<ServerPlayer> getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) {
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public ReferenceList<ServerPlayer> getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) {
return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
return chunk == null ? null : chunk.players[type.ordinal()];
}
public static final class TrackedChunk {
private static final ServerPlayer[] EMPTY_PLAYERS_ARRAY = new ServerPlayer[0];
private final long chunkKey;
private final NearbyPlayers nearbyPlayers;
private final ReferenceList<ServerPlayer>[] players = new ReferenceList[TOTAL_MAP_TYPES];
private int nonEmptyLists;
private long updateCount;
public TrackedChunk(final long chunkKey, final NearbyPlayers nearbyPlayers) {
this.chunkKey = chunkKey;
this.nearbyPlayers = nearbyPlayers;
}
public boolean isEmpty() {
return this.nonEmptyLists == 0;
}
@@ -188,10 +145,7 @@ public final class NearbyPlayers {
final ReferenceList<ServerPlayer> list = this.players[idx];
if (list == null) {
++this.nonEmptyLists;
final ReferenceList<ServerPlayer> players = (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY));
this.nearbyPlayers.directByChunk[idx].put(this.chunkKey, players);
players.add(player);
(this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)).add(player);
return;
}
@@ -215,7 +169,6 @@ public final class NearbyPlayers {
if (list.size() == 0) {
this.players[idx] = null;
this.nearbyPlayers.directByChunk[idx].remove(this.chunkKey);
--this.nonEmptyLists;
}
}
@@ -234,19 +187,9 @@ public final class NearbyPlayers {
protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey);
final NearbyMapType type = this.type;
if (chunk != null) {
chunk.addPlayer(parameter, type);
type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
} else {
final TrackedChunk created = new TrackedChunk(chunkKey, NearbyPlayers.this);
NearbyPlayers.this.byChunk.put(chunkKey, created);
created.addPlayer(parameter, type);
type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$requestChunkData(chunkKey).nearbyPlayers = created;
}
NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> {
return new TrackedChunk();
}).addPlayer(parameter, this.type);
}
@Override
@@ -258,16 +201,10 @@ public final class NearbyPlayers {
throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey));
}
final NearbyMapType type = this.type;
chunk.removePlayer(parameter, type);
type.removeFrom(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
chunk.removePlayer(parameter, this.type);
if (chunk.isEmpty()) {
NearbyPlayers.this.byChunk.remove(chunkKey);
final ChunkData chunkData = ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$releaseChunkData(chunkKey);
if (chunkData != null) {
chunkData.nearbyPlayers = null;
}
}
}
}

View File

@@ -1,8 +1,7 @@
package ca.spottedleaf.moonrise.common.misc;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
@@ -15,24 +14,16 @@ public final class PositionCountingAreaMap<T> {
return this.counters.keySet();
}
public LongSet getPositions() {
return this.positions.keySet();
}
public int getTotalPositions() {
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(CoordinateUtils.getChunkKey(toX, toZ));
return this.positions.containsKey(IntPairUtil.key(toX, toZ));
}
public int getObjectsNear(final int toX, final int toZ) {
return this.positions.get(CoordinateUtils.getChunkKey(toX, toZ));
return this.positions.get(IntPairUtil.key(toX, toZ));
}
public boolean add(final T parameter, final int toX, final int toZ, final int distance) {
@@ -89,12 +80,12 @@ public final class PositionCountingAreaMap<T> {
@Override
protected void addCallback(final T parameter, final int toX, final int toZ) {
PositionCountingAreaMap.this.positions.addTo(CoordinateUtils.getChunkKey(toX, toZ), 1);
PositionCountingAreaMap.this.positions.addTo(IntPairUtil.key(toX, toZ), 1);
}
@Override
protected void removeCallback(final T parameter, final int toX, final int toZ) {
final long key = CoordinateUtils.getChunkKey(toX, toZ);
final long key = IntPairUtil.key(toX, toZ);
if (PositionCountingAreaMap.this.positions.addTo(key, -1) == 1) {
PositionCountingAreaMap.this.positions.remove(key);
}

View File

@@ -1,5 +1,7 @@
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;
@@ -97,8 +99,8 @@ public abstract class SingleUserAreaMap<T> {
final int dx = toX - fromX;
final int dz = toZ - fromZ;
final int totalX = Math.abs(fromX - toX);
final int totalZ = Math.abs(fromZ - toZ);
final int totalX = IntegerUtil.branchlessAbs(fromX - toX);
final int totalZ = IntegerUtil.branchlessAbs(fromZ - toZ);
if (Math.max(totalX, totalZ) > (2 * Math.max(newViewDistance, oldViewDistance))) {
// teleported
@@ -118,7 +120,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(Math.abs(currX - toX), Math.abs(currZ - toZ)) > newViewDistance) {
if (Math.max(IntegerUtil.branchlessAbs(currX - toX), IntegerUtil.branchlessAbs(currZ - toZ)) > newViewDistance) {
this.removeCallback(parameter, currX, currZ);
}
}
@@ -134,7 +136,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(Math.abs(currX - fromX), Math.abs(currZ - fromZ)) > oldViewDistance) {
if (Math.max(IntegerUtil.branchlessAbs(currX - fromX), IntegerUtil.branchlessAbs(currZ - fromZ)) > oldViewDistance) {
this.addCallback(parameter, currX, currZ);
}
}

View File

@@ -1,176 +0,0 @@
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

@@ -0,0 +1,173 @@
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 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()).startTickingChunk(chunk);
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
}
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
}
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

@@ -1,80 +0,0 @@
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,9 +1,10 @@
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.yamlconfig.adapter.TypeAdapterRegistry;
import ca.spottedleaf.yamlconfig.config.YamlConfig;
import ca.spottedleaf.moonrise.common.config.moonrise.adapter.DefaultedTypeAdapter;
import ca.spottedleaf.moonrise.common.config.moonrise.type.DefaultedValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
@@ -12,17 +13,19 @@ public final class ConfigHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
private static final File CONFIG_FILE = new File(System.getProperty(PlatformHooks.get().getBrand() + ".ConfigFile", "config/moonrise.yml"));
private static final File CONFIG_FILE = new File(System.getProperty("Moonrise.ConfigFile", "config/moonrise.yml"));
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) {
throw new RuntimeException(ex);
}
}
private static final String CONFIG_HEADER = String.format("""
private static final String CONFIG_HEADER = """
This is the configuration file for Moonrise.
Each configuration option is prefixed with a comment to explain what it does. Additional changes to this file
@@ -30,10 +33,9 @@ public final class ConfigHolder {
Below are the Moonrise startup flags. Note that startup flags must be placed in the JVM arguments, not
program arguments.
-D%1$s.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
-D%1$s.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
-D%1$s.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
""", PlatformHooks.get().getBrand());
-DMoonrise.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
-DMoonrise.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
""";
static {
reloadConfig();
@@ -48,6 +50,14 @@ 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 {
@@ -58,8 +68,6 @@ public final class ConfigHolder {
}
}
CONFIG.callInitialisers();
// write back any changes, or create if needed
return saveConfig();
}

View File

@@ -1,44 +0,0 @@
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,7 +1,6 @@
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;
@@ -17,7 +16,7 @@ public final class JsonUtil {
final StringWriter stringWriter = new StringWriter();
final JsonWriter jsonWriter = new JsonWriter(stringWriter);
jsonWriter.setIndent(" ");
jsonWriter.setStrictness(Strictness.LENIENT);
jsonWriter.setLenient(false);
Streams.write(element, jsonWriter);
final String jsonString = stringWriter.toString();

View File

@@ -3,12 +3,8 @@ package ca.spottedleaf.moonrise.common.util;
public final class MixinWorkarounds {
// mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
// https://github.com/FabricMC/Mixin/pull/147
public static long[] clone(final long[] values) {
return values.clone();
}
public static byte[] clone(final byte[] values) {
return values.clone();
}
}

View File

@@ -19,7 +19,7 @@ public final class MoonriseCommon {
@Override
public void accept(Thread thread) {
thread.setDaemon(true);
thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
thread.setName("Moonrise Common Worker #" + this.idGenerator.getAndIncrement());
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread thread, final Throwable throwable) {
@@ -44,7 +44,7 @@ public final class MoonriseCommon {
} else {
defaultWorkerThreads = defaultWorkerThreads / 2;
}
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
defaultWorkerThreads = Integer.getInteger("Moonrise.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
int workerThreads = configWorkerThreads;

View File

@@ -1,10 +1,8 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.moonrise.common.PlatformHooks;
public final class MoonriseConstants {
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
public static final int MAX_VIEW_DISTANCE = 32;
private MoonriseConstants() {}

View File

@@ -0,0 +1,53 @@
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

@@ -1,105 +0,0 @@
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

@@ -1,94 +0,0 @@
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,81 +15,56 @@ 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 failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
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);
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
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);
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
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);
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
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);
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
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);
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
@@ -130,10 +105,6 @@ 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

@@ -8,19 +8,11 @@ public final class WorldUtil {
// min, max are inclusive
public static int getMaxSection(final LevelHeightAccessor world) {
return world.getMaxSectionY();
}
public static int getMaxSection(final Level world) {
return world.getMaxSectionY();
return world.getMaxSection() - 1; // getMaxSection() is exclusive
}
public static int getMinSection(final LevelHeightAccessor world) {
return world.getMinSectionY();
}
public static int getMinSection(final Level world) {
return world.getMinSectionY();
return world.getMinSection();
}
public static int getMaxLightSection(final LevelHeightAccessor world) {

View File

@@ -3,7 +3,6 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import net.minecraft.util.BitStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -19,15 +18,15 @@ interface BitStorageMixin extends BlockCountingBitStorage {
// provide default impl in case mods implement this...
@Override
public default Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>();
public default Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>();
final int size = this.getSize();
for (int index = 0; index < size; ++index) {
final int paletteIdx = this.get(index);
ret.computeIfAbsent(paletteIdx, (final int key) -> {
return new ShortArrayList(64);
}).add((short)index);
return new IntArrayList();
}).add(index);
}
return ret;

View File

@@ -1,13 +1,13 @@
package ca.spottedleaf.moonrise.mixin.block_counting;
import ca.spottedleaf.moonrise.common.list.ShortList;
import ca.spottedleaf.moonrise.common.list.IntList;
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
import com.llamalad7.mixinextras.sugar.Local;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.minecraft.util.BitStorage;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunkSection;
@@ -48,32 +48,26 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
public abstract boolean maybeHas(Predicate<BlockState> predicate);
@Unique
private static final ShortArrayList FULL_LIST = new ShortArrayList(16*16*16);
private static final IntArrayList FULL_LIST = new IntArrayList(16*16*16);
static {
for (short i = 0; i < (16*16*16); ++i) {
for (int i = 0; i < (16*16*16); ++i) {
FULL_LIST.add(i);
}
}
@Unique
private boolean isClient;
private int specialCollidingBlocks;
@Unique
private static final short CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS = (short)9999;
@Unique
private short specialCollidingBlocks;
@Unique
private final ShortList tickingBlocks = new ShortList();
private final IntList tickingBlocks = new IntList();
@Override
public final boolean moonrise$hasSpecialCollidingBlocks() {
return this.specialCollidingBlocks != 0;
public final int moonrise$getSpecialCollidingBlocks() {
return this.specialCollidingBlocks;
}
@Override
public final ShortList moonrise$getTickingBlockList() {
public final IntList moonrise$getTickingBlockList() {
return this.tickingBlocks;
}
@@ -92,35 +86,20 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
if (oldState == newState) {
return;
}
if (this.isClient) {
if (CollisionUtil.isSpecialCollidingBlock(newState)) {
this.specialCollidingBlocks = CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS;
}
return;
if (CollisionUtil.isSpecialCollidingBlock(oldState)) {
--this.specialCollidingBlocks;
}
if (CollisionUtil.isSpecialCollidingBlock(newState)) {
++this.specialCollidingBlocks;
}
final boolean isSpecialOld = CollisionUtil.isSpecialCollidingBlock(oldState);
final boolean isSpecialNew = CollisionUtil.isSpecialCollidingBlock(newState);
if (isSpecialOld != isSpecialNew) {
if (isSpecialOld) {
--this.specialCollidingBlocks;
} else {
++this.specialCollidingBlocks;
}
final int position = x | (z << 4) | (y << (4+4));
if (oldState.isRandomlyTicking()) {
this.tickingBlocks.remove(position);
}
final boolean oldTicking = oldState.isRandomlyTicking();
final boolean newTicking = newState.isRandomlyTicking();
if (oldTicking != newTicking) {
final ShortList tickingBlocks = this.tickingBlocks;
final short position = (short)(x | (z << 4) | (y << (4+4)));
if (oldTicking) {
tickingBlocks.remove(position);
} else {
tickingBlocks.add(position);
}
if (newState.isRandomlyTicking()) {
this.tickingBlocks.add(position);
}
}
@@ -154,11 +133,11 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
final PalettedContainer.Data<BlockState> data = this.states.data;
final Palette<BlockState> palette = data.palette();
final Palette<BlockState> palette = data.palette;
final int paletteSize = palette.getSize();
final BitStorage storage = data.storage();
final BitStorage storage = data.storage;
final Int2ObjectOpenHashMap<ShortArrayList> counts;
final Int2ObjectOpenHashMap<IntArrayList> counts;
if (paletteSize == 1) {
counts = new Int2ObjectOpenHashMap<>(1);
counts.put(0, FULL_LIST);
@@ -166,10 +145,10 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
counts = ((BlockCountingBitStorage)storage).moonrise$countEntries();
}
for (final Iterator<Int2ObjectMap.Entry<ShortArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
final Int2ObjectMap.Entry<ShortArrayList> entry = iterator.next();
for (final Iterator<Int2ObjectMap.Entry<IntArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
final Int2ObjectMap.Entry<IntArrayList> entry = iterator.next();
final int paletteIdx = entry.getIntKey();
final ShortArrayList coordinates = entry.getValue();
final IntArrayList coordinates = entry.getValue();
final int paletteCount = coordinates.size();
final BlockState state = palette.valueFor(paletteIdx);
@@ -179,21 +158,16 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
}
if (CollisionUtil.isSpecialCollidingBlock(state)) {
this.specialCollidingBlocks += (short)paletteCount;
this.specialCollidingBlocks += paletteCount;
}
this.nonEmptyBlockCount += (short)paletteCount;
this.nonEmptyBlockCount += paletteCount;
if (state.isRandomlyTicking()) {
this.tickingBlockCount += (short)paletteCount;
final short[] raw = coordinates.elements();
final int rawLen = raw.length;
this.tickingBlockCount += paletteCount;
final int[] raw = coordinates.elements();
final ShortList tickingBlocks = this.tickingBlocks;
tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16));
Objects.checkFromToIndex(0, paletteCount, rawLen);
Objects.checkFromToIndex(0, paletteCount, raw.length);
for (int i = 0; i < paletteCount; ++i) {
tickingBlocks.add(raw[i]);
this.tickingBlocks.add(raw[i]);
}
}
@@ -202,7 +176,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
if (!fluid.isEmpty()) {
//this.nonEmptyBlockCount += count; // fix vanilla bug: make non-empty block count correct
if (fluid.isRandomlyTicking()) {
this.tickingFluidCount += (short)paletteCount;
this.tickingFluidCount += paletteCount;
}
}
}
@@ -210,7 +184,7 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
}
/**
* @reason Set up special colliding blocks on the client, as it is too expensive to perform a full calculation
* @reason Call recalcBlockCounts on the client, as the client does not invoke it when deserializing chunk sections.
* @author Spottedleaf
*/
@Inject(
@@ -220,8 +194,6 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
)
)
private void callRecalcBlocksClient(final CallbackInfo ci) {
this.isClient = true;
// force has special colliding blocks to be true
this.specialCollidingBlocks = this.nonEmptyBlockCount != (short)0 && this.maybeHas(CollisionUtil::isSpecialCollidingBlock) ? CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS : (short)0;
this.recalcBlockCounts();
}
}

View File

@@ -3,7 +3,6 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import net.minecraft.util.BitStorage;
import net.minecraft.util.SimpleBitStorage;
import org.spongepowered.asm.mixin.Final;
@@ -25,66 +24,43 @@ abstract class SimpleBitStorageMixin implements BitStorage, BlockCountingBitStor
@Final
private int bits;
@Shadow
@Final
private long mask;
@Shadow
@Final
private int size;
@Override
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
final int valuesPerLong = this.valuesPerLong;
final int bits = this.bits;
final long mask = (1L << bits) - 1L;
final long mask = this.mask;
final int size = this.size;
if (bits <= 6) {
final ShortArrayList[] byId = new ShortArrayList[1 << bits];
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1 << bits);
// we may be backed by global palette, so limit bits for init capacity
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(
1 << Math.min(6, bits)
);
int index = 0;
int index = 0;
for (long value : this.data) {
int li = 0;
do {
final int paletteIdx = (int)(value & mask);
value >>= bits;
++li;
for (long value : this.data) {
int li = 0;
do {
final int paletteIdx = (int)(value & mask);
value >>= bits;
final ShortArrayList coords = byId[paletteIdx];
if (coords != null) {
coords.add((short)index++);
continue;
} else {
final ShortArrayList newCoords = new ShortArrayList(64);
byId[paletteIdx] = newCoords;
newCoords.add((short)index++);
ret.put(paletteIdx, newCoords);
continue;
}
} while (li < valuesPerLong && index < size);
}
ret.computeIfAbsent(paletteIdx, (final int key) -> {
return new IntArrayList();
}).add(index);
return ret;
} else {
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(
1 << 6
);
int index = 0;
for (long value : this.data) {
int li = 0;
do {
final int paletteIdx = (int)(value & mask);
value >>= bits;
++li;
ret.computeIfAbsent(paletteIdx, (final int key) -> {
return new ShortArrayList(64);
}).add((short)index++);
} while (li < valuesPerLong && index < size);
}
return ret;
++li;
++index;
} while (li < valuesPerLong && index < size);
}
return ret;
}
}

View File

@@ -3,7 +3,6 @@ package ca.spottedleaf.moonrise.mixin.block_counting;
import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import net.minecraft.util.BitStorage;
import net.minecraft.util.ZeroBitStorage;
import org.spongepowered.asm.mixin.Final;
@@ -18,17 +17,17 @@ abstract class ZeroBitStorageMixin implements BitStorage, BlockCountingBitStorag
private int size;
@Override
public final Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries() {
public final Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries() {
final int size = this.size;
final short[] raw = new short[size];
final int[] raw = new int[size];
for (int i = 0; i < size; ++i) {
raw[i] = (short)i;
raw[i] = i;
}
final ShortArrayList coordinates = ShortArrayList.wrap(raw, size);
final IntArrayList coordinates = IntArrayList.wrap(raw, size);
final Int2ObjectOpenHashMap<ShortArrayList> ret = new Int2ObjectOpenHashMap<>(1);
final Int2ObjectOpenHashMap<IntArrayList> ret = new Int2ObjectOpenHashMap<>(1);
ret.put(0, coordinates);
return ret;
}

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

@@ -6,7 +6,6 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.Property;
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.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -18,9 +17,6 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
super(string, class_);
}
@Unique
private static final Boolean[] BY_ID = new Boolean[]{ Boolean.FALSE, Boolean.TRUE };
@Override
public final int moonrise$getIdFor(final Boolean value) {
return value.booleanValue() ? 1 : 0;
@@ -37,6 +33,23 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
)
)
private void init(final CallbackInfo ci) {
this.moonrise$setById(BY_ID);
this.moonrise$setById(new Boolean[]{ Boolean.FALSE, Boolean.TRUE });
}
/**
* This skips all ops after the identity comparison in the original code.
*
* @reason Properties are identity comparable
* @author Spottedleaf
*/
@WrapOperation(
method = "equals",
constant = @Constant(
classValue = BooleanProperty.class,
ordinal = 0
)
)
private boolean skipFurtherComparison(final Object obj, final Operation<Boolean> orig) {
return false;
}
}

View File

@@ -18,6 +18,9 @@ import java.util.Collection;
@Mixin(EnumProperty.class)
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {
@Shadow
public abstract Collection<T> getPossibleValues();
@Unique
private int[] idLookupTable;

View File

@@ -83,6 +83,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
}
// remove values arrays
this.values = null;
for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
final S value = entry.getValue();
((StateHolderMixin<O, S>)(Object)(StateHolder<O, S>)value).values = null;
@@ -125,8 +126,8 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
* @author Spottedleaf
*/
@Overwrite
public <T extends Comparable<T>> T getNullableValue(final Property<T> property) {
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
public <T extends Comparable<T>> Optional<T> getOptionalValue(final Property<T> property) {
return property == null ? Optional.empty() : Optional.ofNullable(this.optimisedTable.get(this.tableIndex, property));
}
/**
@@ -166,7 +167,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
*/
@Overwrite
public Map<Property<?>, Comparable<?>> getValues() {
final ZeroCollidingReferenceStateTable<O, S> table = this.optimisedTable;
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

@@ -1,4 +1,4 @@
package ca.spottedleaf.moonrise.mixin.getblock;
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import net.minecraft.core.Holder;

View File

@@ -1,7 +1,7 @@
package ca.spottedleaf.moonrise.mixin.getblock;
package ca.spottedleaf.moonrise.mixin.chunk_getblock;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
import ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.world.level.ChunkPos;
@@ -81,7 +81,6 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
* @reason Route to optimized getBlock
* @author Spottedleaf
*/
@Override
@Overwrite
public BlockState getBlockState(final BlockPos pos) {
return this.moonrise$getBlock(pos.getX(), pos.getY(), pos.getZ());
@@ -102,7 +101,7 @@ abstract class LevelChunkMixin extends ChunkAccess implements GetBlockChunk {
}
@Override
public final BlockState moonrise$getBlock(final int x, final int y, final int z) {
public BlockState moonrise$getBlock(final int x, final int y, final int z) {
if (this.debug) {
return this.getBlockDebug(x, y, z);
}

View File

@@ -302,10 +302,6 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
)
)
private LevelChunk redirectLightUpdate(final ChunkHolder instance) {
if (this.playersSentChunkTo.size() == 0) {
// no players to sent to, so don't need to update anything
return null;
}
return this.getChunkToSend();
}
@@ -366,7 +362,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
/**
* @reason Use ticket system to control ticket levels
* @author Spottedleaf
* @see net.minecraft.server.level.ServerChunkCache#addTicketWithRadius(TicketType, ChunkPos, int)
* @see net.minecraft.server.level.ServerChunkCache#addRegionTicket(TicketType, ChunkPos, int, Object)
*/
@Overwrite
public void setTicketLevel(int i) {
@@ -397,14 +393,8 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
* @reason Chunk system hooks for ticket level updating now in {@link NewChunkHolder#processTicketLevelUpdate(List, List)}
* @author Spottedleaf
*/
// 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) {
@Overwrite
public void updateFutures(final ChunkMap chunkMap, final Executor executor) {
throw new UnsupportedOperationException();
}

View File

@@ -2,7 +2,6 @@ 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;
@@ -16,8 +15,8 @@ abstract class ChunkMap$DistanceManagerMixin extends net.minecraft.server.level.
@Final
ChunkMap field_17443;
protected ChunkMap$DistanceManagerMixin(final TicketStorage p_394060_, final Executor p_140774_, final Executor p_140775_) {
super(p_394060_, p_140774_, p_140775_);
protected ChunkMap$DistanceManagerMixin(Executor executor, Executor executor2) {
super(executor, executor2);
}
/**

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;
@@ -11,17 +11,14 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import com.mojang.datafixers.DataFixer;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntConsumer;
import java.util.function.Supplier;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.StreamTagVisitor;
import net.minecraft.server.level.ChunkGenerationTask;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ChunkResult;
import net.minecraft.server.level.ChunkTaskDispatcher;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.ChunkTrackingView;
import net.minecraft.server.level.GeneratingChunkMap;
import net.minecraft.server.level.GenerationChunkHolder;
@@ -31,8 +28,8 @@ import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.Mth;
import net.minecraft.util.StaticCache2D;
import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.util.thread.ProcessorHandle;
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;
@@ -44,6 +41,7 @@ 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;
@@ -80,10 +78,13 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
@Shadow
private ChunkTaskDispatcher worldgenTaskDispatcher;
private ChunkTaskPriorityQueueSorter queueSorter;
@Shadow
private ChunkTaskDispatcher lightTaskDispatcher;
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
@Shadow
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
@Shadow
private int serverViewDistance;
@@ -97,12 +98,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
@Shadow
private Queue<Runnable> unloadQueue;
@Shadow
private LongSet chunksToEagerlySave;
@Shadow
private AtomicInteger activeChunkWrites;
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
super(regionStorageInfo, path, dataFixer, bl);
}
@@ -124,27 +119,25 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
)
private void constructor(
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) {
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) {
// intentionally destroy old chunk system hooks
this.updatingChunkMap = null;
this.visibleChunkMap = null;
this.pendingUnloads = null;
this.worldgenTaskDispatcher = null;
this.lightTaskDispatcher = null;
this.queueSorter = null;
this.worldgenMailbox = null;
this.mainThreadMailbox = null;
this.pendingGenerationTasks = null;
this.unloadQueue = null;
this.chunksToEagerlySave = null;
this.activeChunkWrites = null;
// Dummy impl for mods that try to loadAsync directly
this.worker = new IOWorker(
// copied from super call
new RegionStorageInfo(p_214837_.getLevelId(), p_214836_.dimension(), "chunk"),
p_214837_.getDimensionPath(p_214836_.dimension()).resolve("region"), p_214848_
new RegionStorageInfo(arg2.getLevelId(), arg.dimension(), "chunk"), arg2.getDimensionPath(arg.dimension()).resolve("region"), bl
) {
@Override
public boolean isOldChunkAround(final ChunkPos chunkPos, final int i) {
@@ -159,7 +152,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
@Override
public CompletableFuture<Optional<CompoundTag>> loadAsync(final ChunkPos chunkPos) {
final CompletableFuture<Optional<CompoundTag>> future = new CompletableFuture<>();
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (final CompoundTag tag, final Throwable throwable) -> {
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (tag, throwable) -> {
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
@@ -191,15 +184,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
};
}
/**
* @reason This map is not needed, we maintain our own ordered set of chunks to autosave.
* @author Spottedleaf
*/
@Overwrite
public void setChunkUnsaved(final ChunkPos pos) {
}
/**
* @reason Route to new chunk system hooks
* @author Spottedleaf
@@ -277,15 +261,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void onLevelChange(final ChunkPos chunkPos, final IntSupplier intSupplier, final int i, final IntConsumer intConsumer) {
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
@@ -334,15 +309,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void saveChunksEagerly(final BooleanSupplier hasTime) {
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
@@ -437,7 +403,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @author Spottedleaf
*/
@Overwrite
public void onChunkReadyToSend(final ChunkHolder holder, final LevelChunk chunk) {
public void onChunkReadyToSend(final LevelChunk chunk) {
throw new UnsupportedOperationException();
}
@@ -456,7 +422,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @see NewChunkHolder#save(boolean)
*/
@Overwrite
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder, final long time) {
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder) {
throw new UnsupportedOperationException();
}
@@ -500,7 +466,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
*/
@Overwrite
public int getPlayerViewDistance(final ServerPlayer player) {
return PlatformHooks.get().getSendViewDistance(player);
return ChunkSystem.getSendViewDistance(player);
}
/**
@@ -539,39 +505,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
throw new UnsupportedOperationException();
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Redirect(
method = "collectSpawningChunks",
at = @At(
value = "INVOKE",
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
)
)
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);
}
@Override
public CompletableFuture<Optional<CompoundTag>> read(final ChunkPos pos) {
final CompletableFuture<Optional<CompoundTag>> ret = new CompletableFuture<>();
@@ -591,11 +524,10 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
}
@Override
public CompletableFuture<Void> write(final ChunkPos pos, final Supplier<CompoundTag> tag) {
public CompletableFuture<Void> write(final ChunkPos pos, final CompoundTag tag) {
MoonriseRegionFileIO.scheduleSave(
this.level, pos.x, pos.z, tag.get(),
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA
);
this.level, pos.x, pos.z, tag,
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
return null;
}
@@ -616,7 +548,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
)
private void avoidUpdateChunkTrackingInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer) {
PlatformHooks.get().addPlayerToDistanceMaps(this.level, serverPlayer);
ChunkSystem.addPlayerToDistanceMaps(this.level, serverPlayer);
}
/**
@@ -646,7 +578,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
private void avoidApplyChunkTrackingViewInUpdate(final ChunkMap instance, final ServerPlayer serverPlayer,
final ChunkTrackingView chunkTrackingView) {
PlatformHooks.get().removePlayerFromDistanceMaps(this.level, serverPlayer);
ChunkSystem.removePlayerFromDistanceMaps(this.level, serverPlayer);
}
/**
@@ -660,7 +592,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
)
)
private void updateMapsHook(final ServerPlayer player, final CallbackInfo ci) {
PlatformHooks.get().updateMaps(this.level, player);
ChunkSystem.updateMaps(this.level, player);
}
/**

View File

@@ -3,13 +3,13 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
import net.minecraft.core.SectionPos;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(SerializableChunkData.class)
abstract class SerializableChunkDataMixin {
@Mixin(ChunkSerializer.class)
abstract class ChunkSerializerMixin {
/**
* @reason Chunk system handles this during full transition
@@ -17,12 +17,12 @@ abstract class SerializableChunkDataMixin {
* @see ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask
*/
@Redirect(
method = "read",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
)
method = "read",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
)
)
private void skipConsistencyCheck(final PoiManager instance, final SectionPos sectionPos, final LevelChunkSection levelChunkSection) {}
private static void skipConsistencyCheck(PoiManager instance, SectionPos sectionPos, LevelChunkSection levelChunkSection) {}
}

View File

@@ -22,13 +22,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
@Mixin(ChunkStorage.class)
abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseable {
@Shadow
public IOWorker worker;
private IOWorker worker;
@Unique
private static final Logger LOGGER = LogUtils.getLogger();
@@ -119,13 +118,13 @@ abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseab
method = "write",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;"
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
)
)
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
final Supplier<CompoundTag> compoundTag) {
final CompoundTag compoundTag) {
try {
this.storage.write(chunkPos, compoundTag.get());
this.storage.write(chunkPos, compoundTag);
return CompletableFuture.completedFuture(null);
} catch (final Throwable throwable) {
return CompletableFuture.failedFuture(throwable);

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