Compare commits
19 Commits
v0.2.0-bet
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
206255e9f5 | ||
|
|
40a9f1cfcd | ||
|
|
b4789a91f9 | ||
|
|
97b84f0ccc | ||
|
|
1470211fe3 | ||
|
|
4d3b4a52a6 | ||
|
|
a746449be8 | ||
|
|
13948cdf26 | ||
|
|
ac0c7deb43 | ||
|
|
16c8398d8a | ||
|
|
661ef813bb | ||
|
|
cf1d26a73c | ||
|
|
0cbff02a1c | ||
|
|
ce4ee767fe | ||
|
|
d31b15122f | ||
|
|
ca931e842b | ||
|
|
c2cf985899 | ||
|
|
d270cf06d9 | ||
|
|
09735958c0 |
@@ -68,19 +68,19 @@ allprojects {
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "https://repo.papermc.io/repository/maven-public/"
|
||||
url = "https://repo.papermc.io/repository/maven-public/"
|
||||
mavenContent {
|
||||
includeGroup("ca.spottedleaf")
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://api.modrinth.com/maven"
|
||||
url = "https://api.modrinth.com/maven"
|
||||
mavenContent {
|
||||
includeGroup("maven.modrinth")
|
||||
}
|
||||
}
|
||||
maven { url "https://maven.shedaniel.me/" }
|
||||
maven { url "https://maven.terraformersmc.com/releases/" }
|
||||
maven { url = "https://maven.shedaniel.me/" }
|
||||
maven { url = "https://maven.terraformersmc.com/releases/" }
|
||||
}
|
||||
|
||||
// make build reproducible
|
||||
|
||||
@@ -2,11 +2,17 @@ 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;
|
||||
@@ -66,12 +72,29 @@ public abstract class Aw2AtTask extends DefaultTask {
|
||||
final AccessTransformSet accessTransformSet = toAccessTransformSet(reader);
|
||||
Files.deleteIfExists(this.getOutputFile().get().getAsFile().toPath());
|
||||
Files.createDirectories(this.getOutputFile().get().getAsFile().toPath().getParent());
|
||||
AccessTransformFormats.FML.write(this.getOutputFile().get().getAsFile().toPath(), accessTransformSet);
|
||||
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
|
||||
@@ -98,8 +121,7 @@ public abstract class Aw2AtTask extends DefaultTask {
|
||||
*/
|
||||
|
||||
public static AccessTransformSet toAccessTransformSet(final BufferedReader reader) throws IOException {
|
||||
// TODO: Remove copied classes once https://github.com/architectury/at/pull/1 is released
|
||||
AccessTransformSet atSet = new at.AccessTransformSetImpl();
|
||||
AccessTransformSet atSet = AccessTransformSet.create();
|
||||
|
||||
new AccessWidenerReader(new AccessWidenerVisitor() {
|
||||
@Override
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Architectury
|
||||
* Copyright (c) 2018 Minecrell (https://github.com/Minecrell)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package at;
|
||||
|
||||
import dev.architectury.at.AccessTransformSet;
|
||||
import net.fabricmc.mappingio.tree.MappingTreeView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AccessTransformSetImpl implements AccessTransformSet {
|
||||
|
||||
private final Map<String, Class> classes = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public Map<String, Class> getClasses() {
|
||||
return Collections.unmodifiableMap(this.classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Class> getClass(String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return Optional.ofNullable(this.classes.get(name.replace('.', '/')));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getOrCreateClass(String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return this.classes.computeIfAbsent(name.replace('.', '/'), n -> new ClassAccessTransformSetImpl(this, n));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Class> removeClass(String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
return Optional.ofNullable(this.classes.remove(name.replace('.', '/')));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransformSet remap(MappingTreeView mappings, String from, String to) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(AccessTransformSet other) {
|
||||
other.getClasses().forEach((name, classSet) -> getOrCreateClass(name).merge(classSet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof AccessTransformSetImpl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessTransformSetImpl that = (AccessTransformSetImpl) o;
|
||||
return this.classes.equals(that.classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.classes.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AccessTransformSet{" + classes + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2023 Architectury
|
||||
* Copyright (c) 2018 Minecrell (https://github.com/Minecrell)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package at;
|
||||
|
||||
import dev.architectury.at.AccessTransform;
|
||||
import dev.architectury.at.AccessTransformSet;
|
||||
import org.cadixdev.bombe.analysis.InheritanceProvider;
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
class ClassAccessTransformSetImpl implements AccessTransformSet.Class {
|
||||
|
||||
private final AccessTransformSet parent;
|
||||
private final String name;
|
||||
|
||||
private AccessTransform classTransform = AccessTransform.EMPTY;
|
||||
private AccessTransform allFields = AccessTransform.EMPTY;
|
||||
private AccessTransform allMethods = AccessTransform.EMPTY;
|
||||
|
||||
private final Map<String, AccessTransform> fields = new LinkedHashMap<>();
|
||||
private final Map<MethodSignature, AccessTransform> methods = new LinkedHashMap<>();
|
||||
|
||||
private boolean complete;
|
||||
|
||||
ClassAccessTransformSetImpl(AccessTransformSet parent, String name) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransformSet getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform get() {
|
||||
return this.classTransform;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform merge(AccessTransform transform) {
|
||||
return this.classTransform = this.classTransform.merge(transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform replace(AccessTransform transform) {
|
||||
return this.classTransform = Objects.requireNonNull(transform, "transform");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform allFields() {
|
||||
return this.allFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform mergeAllFields(AccessTransform transform) {
|
||||
return this.allFields = this.allFields.merge(transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform replaceAllFields(AccessTransform transform) {
|
||||
return this.allFields = Objects.requireNonNull(transform, "transform");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform allMethods() {
|
||||
return this.allMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform mergeAllMethods(AccessTransform transform) {
|
||||
return this.allMethods = this.allMethods.merge(transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform replaceAllMethods(AccessTransform transform) {
|
||||
return this.allMethods = Objects.requireNonNull(transform, "transform");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, AccessTransform> getFields() {
|
||||
return Collections.unmodifiableMap(this.fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform getField(String name) {
|
||||
return this.fields.getOrDefault(Objects.requireNonNull(name, "name"), this.allFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform mergeField(String name, AccessTransform transform) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
Objects.requireNonNull(transform, "transform");
|
||||
|
||||
if (transform.isEmpty()) {
|
||||
return this.fields.getOrDefault(name, AccessTransform.EMPTY);
|
||||
}
|
||||
return this.fields.merge(name, transform, AccessTransform::merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform replaceField(String name, AccessTransform transform) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
Objects.requireNonNull(transform, "transform");
|
||||
|
||||
if (transform.isEmpty()) {
|
||||
return this.fields.remove(name);
|
||||
}
|
||||
return this.fields.put(name, transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MethodSignature, AccessTransform> getMethods() {
|
||||
return Collections.unmodifiableMap(this.methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform getMethod(MethodSignature signature) {
|
||||
return this.methods.getOrDefault(Objects.requireNonNull(signature, "signature"), this.allMethods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform mergeMethod(MethodSignature signature, AccessTransform transform) {
|
||||
Objects.requireNonNull(signature, "signature");
|
||||
Objects.requireNonNull(transform, "transform");
|
||||
|
||||
if (transform.isEmpty()) {
|
||||
return this.methods.getOrDefault(signature, AccessTransform.EMPTY);
|
||||
}
|
||||
return this.methods.merge(signature, transform, AccessTransform::merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessTransform replaceMethod(MethodSignature signature, AccessTransform transform) {
|
||||
Objects.requireNonNull(signature, "signature");
|
||||
Objects.requireNonNull(transform, "transform");
|
||||
|
||||
if (transform.isEmpty()) {
|
||||
return this.methods.remove(signature);
|
||||
}
|
||||
return this.methods.put(signature, transform);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(AccessTransformSet.Class other) {
|
||||
Objects.requireNonNull(other, "other");
|
||||
|
||||
merge(other.get());
|
||||
mergeAllFields(other.allFields());
|
||||
mergeAllMethods(other.allMethods());
|
||||
|
||||
other.getFields().forEach(this::mergeField);
|
||||
other.getMethods().forEach(this::mergeMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return this.complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(InheritanceProvider provider, InheritanceProvider.ClassInfo info) {
|
||||
if (this.complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (InheritanceProvider.ClassInfo parent : info.provideParents(provider)) {
|
||||
AccessTransformSet.Class parentAts = getParent().getOrCreateClass(parent.getName());
|
||||
parentAts.complete(provider, parent);
|
||||
|
||||
parentAts.getMethods().forEach((signature, transform) -> {
|
||||
if (info.overrides(signature, parent)) {
|
||||
mergeMethod(signature, transform);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.complete = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ClassAccessTransformSetImpl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassAccessTransformSetImpl that = (ClassAccessTransformSetImpl) o;
|
||||
return this.classTransform.equals(that.classTransform) &&
|
||||
this.allFields.equals(that.allFields) &&
|
||||
this.allMethods.equals(that.allMethods) &&
|
||||
this.fields.equals(that.fields) &&
|
||||
this.methods.equals(that.methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.classTransform, this.allFields, this.allMethods, this.fields, this.methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringJoiner joiner = new StringJoiner(", ", "AccessTransformSet.Class{", "}");
|
||||
if (!this.classTransform.isEmpty()) {
|
||||
joiner.add(this.classTransform.toString());
|
||||
}
|
||||
if (!this.allFields.isEmpty()) {
|
||||
joiner.add("allFields=" + this.allFields);
|
||||
}
|
||||
if (!this.allMethods.isEmpty()) {
|
||||
joiner.add("allMethods=" + this.allMethods);
|
||||
}
|
||||
if (!this.fields.isEmpty()) {
|
||||
joiner.add("fields=" + this.fields);
|
||||
}
|
||||
if (!this.methods.isEmpty()) {
|
||||
joiner.add("method=" + this.methods);
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,17 @@
|
||||
org.gradle.jvmargs=-Xmx2G
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=true
|
||||
# Fabric Properties
|
||||
# check these on https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.21.4
|
||||
loader_version=0.16.9
|
||||
supported_minecraft_versions=1.21.4
|
||||
neoforge_version=21.4.33-beta
|
||||
neoforge_version=21.4.140
|
||||
neoform_version=1.21.4-20241203.161809
|
||||
fabric_api_version=0.110.5+1.21.4
|
||||
fabric_api_version=0.119.3+1.21.4
|
||||
snakeyaml_version=2.3
|
||||
concurrentutil_version=0.0.2
|
||||
concurrentutil_version=0.0.3
|
||||
yamlconfig_version=1.0.2
|
||||
cloth_version=17.0.144
|
||||
modmenu_version=13.0.0-beta.1
|
||||
@@ -20,6 +21,6 @@ junit_version=5.11.3
|
||||
fabric_lithium_version=t1FlWYl9
|
||||
neo_lithium_version=iDqQi66g
|
||||
# Mod Properties
|
||||
mod_version=0.2.0-beta.7
|
||||
mod_version=0.2.0-beta.11
|
||||
maven_group=ca.spottedleaf.moonrise
|
||||
archives_base_name=moonrise
|
||||
|
||||
@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.config.moonrise;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
|
||||
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||
import ca.spottedleaf.yamlconfig.InitialiseHook;
|
||||
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
|
||||
@@ -38,7 +39,7 @@ public final class MoonriseConfig {
|
||||
|
||||
|
||||
@Adaptable
|
||||
public static final class Basic {
|
||||
public static final class Basic implements InitialiseHook {
|
||||
@Serializable(
|
||||
comment = """
|
||||
The maximum rate of chunks to send to any given player, per second. If this value is <= 0,
|
||||
@@ -72,6 +73,20 @@ public final class MoonriseConfig {
|
||||
section = CHUNK_SYSTEM_SECTION
|
||||
)
|
||||
public double playerMaxGenRate = -1.0;
|
||||
|
||||
@Serializable(
|
||||
comment = """
|
||||
The delay before chunks are unloaded around players once they leave their view distance.
|
||||
The Vanilla value is 0 ticks. Setting this value higher (i.e 5s) will allow pets to teleport
|
||||
to their owners when they teleport.
|
||||
"""
|
||||
)
|
||||
public Duration playerChunkUnloadDelay = Duration.parse("0t");
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
RegionizedPlayerChunkLoader.setUnloadDelay(this.playerChunkUnloadDelay.getTimeTicks());
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable(
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package ca.spottedleaf.moonrise.common.util;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class EntityUtil {
|
||||
|
||||
private static final ThreadLocal<DecimalFormat> THREE_DECIMAL_PLACES = ThreadLocal.withInitial(() -> {
|
||||
return new DecimalFormat("#,##0.000");
|
||||
});
|
||||
|
||||
private static String formatVec(final Vec3 vec) {
|
||||
final DecimalFormat format = THREE_DECIMAL_PLACES.get();
|
||||
|
||||
return "(" + format.format(vec.x) + "," + format.format(vec.y) + "," + format.format(vec.z) + ")";
|
||||
}
|
||||
|
||||
private static String dumpEntityWithoutReferences(final Entity entity) {
|
||||
if (entity == null) {
|
||||
return "{null}";
|
||||
}
|
||||
|
||||
return "{type=" + entity.getClass().getSimpleName() + ",id=" + entity.getId() + ",uuid=" + entity.getUUID() + ",pos=" + formatVec(entity.position())
|
||||
+ ",mot=" + formatVec(entity.getDeltaMovement()) + ",aabb=" + entity.getBoundingBox() + ",removed=" + entity.getRemovalReason() + ",has_vehicle=" + (entity.getVehicle() != null)
|
||||
+ ",passenger_count=" + entity.getPassengers().size();
|
||||
}
|
||||
|
||||
public static String dumpEntity(final Entity entity) {
|
||||
final List<Entity> passengers = entity.getPassengers();
|
||||
final List<String> passengerStrings = new ArrayList<>(passengers.size());
|
||||
|
||||
for (final Entity passenger : passengers) {
|
||||
passengerStrings.add("(" + dumpEntityWithoutReferences(passenger) + ")");
|
||||
}
|
||||
|
||||
return "{root=[" + dumpEntityWithoutReferences(entity) + "], vehicle=[" + dumpEntityWithoutReferences(entity.getVehicle())
|
||||
+ "], passengers=[" + String.join(",", passengerStrings) + "]";
|
||||
}
|
||||
|
||||
private EntityUtil() {}
|
||||
}
|
||||
@@ -15,56 +15,81 @@ public class TickThread extends Thread {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
|
||||
|
||||
private static String getThreadContext() {
|
||||
return "thread=" + Thread.currentThread().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public static void ensureTickThread(final String reason) {
|
||||
if (!isTickThread()) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
LOGGER.error("Thread failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
|
||||
if (!isTickThreadFor(world, pos)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
|
||||
if (!isTickThreadFor(world, pos, blockRadius)) {
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
|
||||
if (!isTickThreadFor(world, pos)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
|
||||
if (!isTickThreadFor(world, chunkX, chunkZ)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Entity entity, final String reason) {
|
||||
if (!isTickThreadFor(entity)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
|
||||
if (!isTickThreadFor(world, aabb)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
|
||||
if (!isTickThreadFor(world, blockX, blockZ)) {
|
||||
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
|
||||
throw new IllegalStateException(reason);
|
||||
final String ex = "Thread failed main thread check: " +
|
||||
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
|
||||
LOGGER.error(ex, new Throwable());
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +130,10 @@ public class TickThread extends Thread {
|
||||
return isTickThread();
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
|
||||
return isTickThread();
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
|
||||
return isTickThread();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package ca.spottedleaf.moonrise.mixin.chunk_system;
|
||||
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(DynamicGameEventListener.class)
|
||||
abstract class DynamicGameEventListenerMixin {
|
||||
@Shadow @Nullable private SectionPos lastSection;
|
||||
|
||||
@Inject(method = "remove", at = @At("RETURN"))
|
||||
private void onRemove(final CallbackInfo ci) {
|
||||
// We need to unset the last section when removed, otherwise if the same instance is re-added at the same position it
|
||||
// will assume there was no change and fail to re-register.
|
||||
// In vanilla, chunks rarely unload and re-load quickly enough to trigger this issue. However, our chunk system has a
|
||||
// quirk where fast chunk reload cycles will often occur on player login (see PR #22).
|
||||
// So we fix this vanilla oversight as our changes cause it to manifest in bugs much more often (see issue #87).
|
||||
this.lastSection = null;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ interface EntityGetterMixin {
|
||||
@Overwrite
|
||||
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
|
||||
if (voxel.isEmpty()) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
||||
|
||||
@@ -55,7 +55,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
||||
public boolean isUnobstructed(final Entity entity) {
|
||||
final AABB boundingBox = entity.getBoundingBox();
|
||||
if (CollisionUtil.isEmpty(boundingBox)) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<Entity> entities = this.getEntities(
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package ca.spottedleaf.moonrise.mixin.serverlist;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.client.multiplayer.resolver.ServerAddress;
|
||||
import net.minecraft.client.multiplayer.resolver.ServerAddressResolver;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@Mixin(ServerAddressResolver.class)
|
||||
@@ -15,24 +18,23 @@ interface ServerAddressResolverMixin {
|
||||
* @author Spottedleaf
|
||||
*/
|
||||
@Redirect(
|
||||
method = {
|
||||
"method_36903",
|
||||
"*(Lnet/minecraft/client/multiplayer/resolver/ServerAddress;)Ljava/util/Optional;"
|
||||
},
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"
|
||||
)
|
||||
method = {
|
||||
"method_36903",
|
||||
"lambda$static$0"
|
||||
},
|
||||
at = @At(
|
||||
value = "NEW",
|
||||
target = "(Ljava/net/InetAddress;I)Ljava/net/InetSocketAddress;"
|
||||
)
|
||||
)
|
||||
private static InetAddress eliminateRDNS(final String name) throws UnknownHostException {
|
||||
final InetAddress ret = InetAddress.getByName(name);
|
||||
|
||||
final byte[] address = ret.getAddress();
|
||||
private static InetSocketAddress eliminateRDNS(InetAddress addr, final int port,
|
||||
@Local(ordinal = 0, argsOnly = true) final ServerAddress serverAddress) throws UnknownHostException {
|
||||
final byte[] address = addr.getAddress();
|
||||
if (address != null) {
|
||||
// pass name to prevent rDNS
|
||||
return InetAddress.getByAddress(name, address);
|
||||
addr = InetAddress.getByAddress(serverAddress.getHost(), address);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return new InetSocketAddress(addr, port);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO {
|
||||
LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr);
|
||||
}
|
||||
|
||||
if (compoundTag == null) {
|
||||
if (throwable == null && compoundTag == null) {
|
||||
// need to re-try from the start
|
||||
this.scheduleReadIO();
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,7 @@ import java.util.function.Function;
|
||||
public final class RegionizedPlayerChunkLoader {
|
||||
|
||||
public static final TicketType<Long> PLAYER_TICKET = TicketType.create("chunk_system:player_ticket", Long::compareTo);
|
||||
public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5 * 20);
|
||||
public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 1);
|
||||
|
||||
public static final int MIN_VIEW_DISTANCE = 2;
|
||||
public static final int MAX_VIEW_DISTANCE = 32;
|
||||
@@ -55,6 +55,10 @@ public final class RegionizedPlayerChunkLoader {
|
||||
public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
|
||||
public static final int TICK_TICKET_LEVEL = ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL;
|
||||
|
||||
public static void setUnloadDelay(final long ticks) {
|
||||
PLAYER_TICKET_DELAYED.timeout = Math.max(1, ticks);
|
||||
}
|
||||
|
||||
public static final class ViewDistanceHolder {
|
||||
|
||||
private volatile ViewDistances viewDistances;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
||||
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||
@@ -81,6 +82,7 @@ public final class ChunkHolderManager {
|
||||
private long currentTick;
|
||||
|
||||
private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>();
|
||||
private final MultiThreadedQueue<NewChunkHolder> offThreadPendingFullLoadUpdate = new MultiThreadedQueue<>();
|
||||
private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
|
||||
if (c1 == c2) {
|
||||
return 0;
|
||||
@@ -109,20 +111,20 @@ public final class ChunkHolderManager {
|
||||
this.unloadQueue = new ChunkUnloadQueue(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift());
|
||||
}
|
||||
|
||||
public boolean processTicketUpdates(final int posX, final int posZ) {
|
||||
public boolean processTicketUpdates(final int chunkX, final int chunkZ) {
|
||||
final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
|
||||
final int ticketMask = (1 << ticketShift) - 1;
|
||||
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
|
||||
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
|
||||
final boolean ret;
|
||||
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
|
||||
((posX >> ticketShift) - 1) << ticketShift,
|
||||
((posZ >> ticketShift) - 1) << ticketShift,
|
||||
(((posX >> ticketShift) + 1) << ticketShift) | ticketMask,
|
||||
(((posZ >> ticketShift) + 1) << ticketShift) | ticketMask
|
||||
((chunkX >> ticketShift) - 1) << ticketShift,
|
||||
((chunkZ >> ticketShift) - 1) << ticketShift,
|
||||
(((chunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
|
||||
(((chunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
|
||||
);
|
||||
try {
|
||||
ret = this.processTicketUpdatesNoLock(posX >> ticketShift, posZ >> ticketShift, scheduledTasks, changedFullStatus);
|
||||
ret = this.processTicketUpdatesNoLock(chunkX >> ticketShift, chunkZ >> ticketShift, scheduledTasks, changedFullStatus);
|
||||
} finally {
|
||||
this.ticketLockArea.unlock(ticketLock);
|
||||
}
|
||||
@@ -720,6 +722,9 @@ public final class ChunkHolderManager {
|
||||
return removeDelay <= 0L;
|
||||
};
|
||||
|
||||
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
|
||||
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
|
||||
|
||||
for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) {
|
||||
final long sectionKey = iterator.nextLong();
|
||||
|
||||
@@ -728,9 +733,16 @@ public final class ChunkHolderManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int lowerChunkX = CoordinateUtils.getChunkX(sectionKey) << sectionShift;
|
||||
final int lowerChunkZ = CoordinateUtils.getChunkZ(sectionKey) << sectionShift;
|
||||
|
||||
final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
|
||||
final int ticketMask = (1 << ticketShift) - 1;
|
||||
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
|
||||
CoordinateUtils.getChunkX(sectionKey) << sectionShift,
|
||||
CoordinateUtils.getChunkZ(sectionKey) << sectionShift
|
||||
((lowerChunkX >> ticketShift) - 1) << ticketShift,
|
||||
((lowerChunkZ >> ticketShift) - 1) << ticketShift,
|
||||
(((lowerChunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
|
||||
(((lowerChunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -777,9 +789,23 @@ public final class ChunkHolderManager {
|
||||
if (chunkToExpireCount.isEmpty()) {
|
||||
this.sectionToChunkToExpireCount.remove(sectionKey);
|
||||
}
|
||||
|
||||
// In order to prevent a race condition where an off-thread invokes processTicketUpdates(), we need to process ticket updates here
|
||||
// so that we catch any additions to the changed full status list. If an off-thread were to process tickets here, it would not be guaranteed
|
||||
// that it would be added to the full changed status set by the end of the call - possibly allowing ticket level decreases to be processed
|
||||
// outside of this call, which is not an intended or expected of this chunk system.
|
||||
this.processTicketUpdatesNoLock(lowerChunkX >> ThreadedTicketLevelPropagator.SECTION_SHIFT, lowerChunkZ >> ThreadedTicketLevelPropagator.SECTION_SHIFT, scheduledTasks, changedFullStatus);
|
||||
} finally {
|
||||
this.ticketLockArea.unlock(ticketLock);
|
||||
}
|
||||
|
||||
this.addChangedStatuses(changedFullStatus);
|
||||
changedFullStatus.clear(); // clear for next loop iteration
|
||||
|
||||
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
|
||||
scheduledTasks.get(i).schedule();
|
||||
}
|
||||
scheduledTasks.clear(); // clear for next loop iteration
|
||||
}
|
||||
|
||||
this.processTicketUpdates();
|
||||
@@ -1006,14 +1032,9 @@ public final class ChunkHolderManager {
|
||||
return;
|
||||
}
|
||||
if (!TickThread.isTickThread()) {
|
||||
this.taskScheduler.scheduleChunkTask(() -> {
|
||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
|
||||
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
||||
pendingFullLoadUpdate.add(changedFullStatus.get(i));
|
||||
}
|
||||
|
||||
ChunkHolderManager.this.processPendingFullUpdate();
|
||||
}, Priority.HIGHEST);
|
||||
// These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
|
||||
// which will invoke processTicketUpdates
|
||||
this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus);
|
||||
} else {
|
||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
||||
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
||||
@@ -1294,36 +1315,20 @@ public final class ChunkHolderManager {
|
||||
}
|
||||
|
||||
public boolean processTicketUpdates() {
|
||||
return this.processTicketUpdates(true, null);
|
||||
}
|
||||
|
||||
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
|
||||
|
||||
static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
|
||||
return CURRENT_TICKET_UPDATE_SCHEDULING.get();
|
||||
}
|
||||
|
||||
private boolean processTicketUpdates(final boolean processFullUpdates, List<ChunkProgressionTask> scheduledTasks) {
|
||||
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
||||
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
|
||||
}
|
||||
if (!PlatformHooks.get().allowAsyncTicketUpdates() && !TickThread.isTickThread()) {
|
||||
final boolean isTickThread = TickThread.isTickThread();
|
||||
|
||||
if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) {
|
||||
TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
|
||||
}
|
||||
|
||||
List<NewChunkHolder> changedFullStatus = null;
|
||||
|
||||
final boolean isTickThread = TickThread.isTickThread();
|
||||
|
||||
boolean ret = false;
|
||||
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
|
||||
final boolean canProcessScheduling = scheduledTasks == null;
|
||||
|
||||
if (this.ticketLevelPropagator.hasPendingUpdates()) {
|
||||
if (scheduledTasks == null) {
|
||||
scheduledTasks = new ArrayList<>();
|
||||
}
|
||||
changedFullStatus = new ArrayList<>();
|
||||
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
|
||||
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
|
||||
|
||||
this.blockTicketUpdates();
|
||||
try {
|
||||
@@ -1334,27 +1339,42 @@ public final class ChunkHolderManager {
|
||||
} finally {
|
||||
this.unblockTicketUpdates(Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
if (changedFullStatus != null) {
|
||||
this.addChangedStatuses(changedFullStatus);
|
||||
}
|
||||
|
||||
if (canProcessScheduling && scheduledTasks != null) {
|
||||
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
|
||||
scheduledTasks.get(i).schedule();
|
||||
}
|
||||
}
|
||||
|
||||
if (canProcessFullUpdates) {
|
||||
if (isTickThread) {
|
||||
ret |= this.processPendingFullUpdate();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
|
||||
|
||||
static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
|
||||
return CURRENT_TICKET_UPDATE_SCHEDULING.get();
|
||||
}
|
||||
|
||||
// only call on tick thread
|
||||
private void processOffThreadFullUpdates() {
|
||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
||||
final MultiThreadedQueue<NewChunkHolder> offThreadPendingFullLoadUpdate = this.offThreadPendingFullLoadUpdate;
|
||||
|
||||
NewChunkHolder toUpdate;
|
||||
while ((toUpdate = offThreadPendingFullLoadUpdate.poll()) != null) {
|
||||
pendingFullLoadUpdate.add(toUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
// only call on tick thread
|
||||
private boolean processPendingFullUpdate() {
|
||||
this.processOffThreadFullUpdates();
|
||||
|
||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
||||
|
||||
boolean ret = false;
|
||||
|
||||
@@ -220,6 +220,7 @@ accessible class net/minecraft/server/level/DistanceManager$FixedPlayerDistanceC
|
||||
# Ticket
|
||||
accessible field net/minecraft/server/level/Ticket key Ljava/lang/Object;
|
||||
accessible field net/minecraft/server/level/TicketType timeout J
|
||||
mutable field net/minecraft/server/level/TicketType timeout J
|
||||
|
||||
|
||||
# ServerChunkCache
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"chunk_system.ChunkStepMixin",
|
||||
"chunk_system.ChunkStorageMixin",
|
||||
"chunk_system.DistanceManagerMixin",
|
||||
"chunk_system.DynamicGameEventListenerMixin",
|
||||
"chunk_system.EntityGetterMixin",
|
||||
"chunk_system.EntityMixin",
|
||||
"chunk_system.EntityTickListMixin",
|
||||
|
||||
Reference in New Issue
Block a user