Compare commits
17 Commits
v0.1.0-bet
...
v0.1.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ea20cde31 | ||
|
|
5e2b1ad7d3 | ||
|
|
3146aa2e3e | ||
|
|
035093d2c6 | ||
|
|
21d106ffa2 | ||
|
|
6841805446 | ||
|
|
eb20846213 | ||
|
|
66a0850ac9 | ||
|
|
9a16a91fb8 | ||
|
|
580041a1a1 | ||
|
|
fb31d0e1f7 | ||
|
|
0b2070d781 | ||
|
|
30a79469e3 | ||
|
|
254b331259 | ||
|
|
a21c44ad31 | ||
|
|
77ba4a4584 | ||
|
|
a510acbd78 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -31,8 +31,6 @@ jobs:
|
|||||||
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
key: ${{ runner.os }}-project-local-gradle-caches-${{ hashFiles('**/libs.versions.toml', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-project-local-gradle-caches-
|
${{ runner.os }}-project-local-gradle-caches-
|
||||||
- name: "setup dependencies"
|
|
||||||
run: ./install_deps.sh
|
|
||||||
- name: "execute gradle build"
|
- name: "execute gradle build"
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
- name: Determine Snapshot Status
|
- name: Determine Snapshot Status
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
|||||||
[submodule "ConcurrentUtil"]
|
|
||||||
path = ConcurrentUtil
|
|
||||||
url = https://github.com/Spottedleaf/ConcurrentUtil.git
|
|
||||||
[submodule "YamlConfig"]
|
|
||||||
path = YamlConfig
|
|
||||||
url = https://github.com/Spottedleaf/YamlConfig.git
|
|
||||||
|
|||||||
Submodule ConcurrentUtil deleted from 08d3ca3241
Submodule YamlConfig deleted from 67552e7707
10
build.gradle
10
build.gradle
@@ -22,8 +22,8 @@ def getGitCommit = { ->
|
|||||||
dependencies {
|
dependencies {
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||||
|
|
||||||
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
api("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
||||||
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
|
api("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
||||||
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
api("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
|
|
||||||
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
|
modImplementation "me.shedaniel.cloth:cloth-config:${rootProject.cloth_version}"
|
||||||
@@ -46,10 +46,10 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal {
|
maven {
|
||||||
|
url "https://repo.papermc.io/repository/maven-public/"
|
||||||
mavenContent {
|
mavenContent {
|
||||||
includeModule("ca.spottedleaf", "concurrentutil")
|
includeGroup("ca.spottedleaf")
|
||||||
includeModule("ca.spottedleaf", "yamlconfig")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ dependencies {
|
|||||||
runtimeOnly(project(":").sourceSets.main.output)
|
runtimeOnly(project(":").sourceSets.main.output)
|
||||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||||
|
|
||||||
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
libs("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
||||||
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
|
libs("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
||||||
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
libs("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
|
|
||||||
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ supported_minecraft_versions=1.21,1.21.1
|
|||||||
loader_version=0.16.5
|
loader_version=0.16.5
|
||||||
neoforge_version=21.1.79
|
neoforge_version=21.1.79
|
||||||
snakeyaml_version=2.3
|
snakeyaml_version=2.3
|
||||||
concurrentutil_version=0.0.2-SNAPSHOT
|
concurrentutil_version=0.0.2
|
||||||
yamlconfig_version=1.0.2-SNAPSHOT
|
yamlconfig_version=1.0.2
|
||||||
cloth_version=15.0.128
|
cloth_version=15.0.128
|
||||||
# version ids from modrinth
|
# version ids from modrinth
|
||||||
fabric_lithium_version=frXUdgvL
|
fabric_lithium_version=frXUdgvL
|
||||||
neo_lithium_version=KhdehJ6l
|
neo_lithium_version=KhdehJ6l
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.1.0-beta.10
|
mod_version=0.1.0-beta.14
|
||||||
maven_group=ca.spottedleaf.moonrise
|
maven_group=ca.spottedleaf.moonrise
|
||||||
archives_base_name=moonrise
|
archives_base_name=moonrise
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -eou pipefail
|
|
||||||
|
|
||||||
git submodule update --init --recursive
|
|
||||||
|
|
||||||
cd ConcurrentUtil
|
|
||||||
mvn install
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
cd YamlConfig
|
|
||||||
mvn install
|
|
||||||
@@ -21,8 +21,8 @@ dependencies {
|
|||||||
add('shadow', project([path: ":", configuration: "namedElements"]))
|
add('shadow', project([path: ":", configuration: "namedElements"]))
|
||||||
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
|
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
|
||||||
|
|
||||||
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}")
|
shadow("ca.spottedleaf:concurrentutil:${rootProject.concurrentutil_version}") { setTransitive(false) }
|
||||||
shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}")
|
shadow("ca.spottedleaf:yamlconfig:${rootProject.yamlconfig_version}") { setTransitive(false) }
|
||||||
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
shadow("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
forgeExtra("org.yaml:snakeyaml:${rootProject.snakeyaml_version}")
|
||||||
|
|
||||||
|
|||||||
@@ -48,3 +48,6 @@ include("fabric")
|
|||||||
findProject(":fabric").name = "Moonrise-Fabric"
|
findProject(":fabric").name = "Moonrise-Fabric"
|
||||||
include("neoforge")
|
include("neoforge")
|
||||||
findProject(":neoforge").name = "Moonrise-NeoForge"
|
findProject(":neoforge").name = "Moonrise-NeoForge"
|
||||||
|
|
||||||
|
// includeBuild("../YamlConfig") // Uncomment to use local YamlConfig
|
||||||
|
// includeBuild("../ConcurrentUtil") // Uncomment to use local ConcurrentUtil
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.common.config.moonrise;
|
|||||||
|
|
||||||
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
|
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
|
||||||
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
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.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
||||||
import ca.spottedleaf.yamlconfig.InitialiseHook;
|
import ca.spottedleaf.yamlconfig.InitialiseHook;
|
||||||
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
|
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
|
||||||
@@ -38,7 +39,7 @@ public final class MoonriseConfig {
|
|||||||
|
|
||||||
|
|
||||||
@Adaptable
|
@Adaptable
|
||||||
public static final class Basic {
|
public static final class Basic implements InitialiseHook {
|
||||||
@Serializable(
|
@Serializable(
|
||||||
comment = """
|
comment = """
|
||||||
The maximum rate of chunks to send to any given player, per second. If this value is <= 0,
|
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
|
section = CHUNK_SYSTEM_SECTION
|
||||||
)
|
)
|
||||||
public double playerMaxGenRate = -1.0;
|
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(
|
@Serializable(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
|
|||||||
completable::complete
|
completable::complete
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!completable.isDone() && chunkTaskScheduler.hasShutdown()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Chunk system has shut down, cannot process chunk requests in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.level) + "' at "
|
||||||
|
+ "(" + chunkX + "," + chunkZ + ") status: " + toStatus
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
|
if (TickThread.isTickThreadFor(this.level, chunkX, chunkZ)) {
|
||||||
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
|
ChunkTaskScheduler.pushChunkWait(this.level, chunkX, chunkZ);
|
||||||
this.mainThreadProcessor.managedBlock(completable::isDone);
|
this.mainThreadProcessor.managedBlock(completable::isDone);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ interface EntityGetterMixin {
|
|||||||
@Overwrite
|
@Overwrite
|
||||||
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
|
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
|
||||||
if (voxel.isEmpty()) {
|
if (voxel.isEmpty()) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
|
|||||||
public boolean isUnobstructed(final Entity entity) {
|
public boolean isUnobstructed(final Entity entity) {
|
||||||
final AABB boundingBox = entity.getBoundingBox();
|
final AABB boundingBox = entity.getBoundingBox();
|
||||||
if (CollisionUtil.isEmpty(boundingBox)) {
|
if (CollisionUtil.isEmpty(boundingBox)) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Entity> entities = this.getEntities(
|
final List<Entity> entities = this.getEntities(
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package ca.spottedleaf.moonrise.mixin.serverlist;
|
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 net.minecraft.client.multiplayer.resolver.ServerAddressResolver;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
@Mixin(ServerAddressResolver.class)
|
@Mixin(ServerAddressResolver.class)
|
||||||
@@ -17,22 +20,21 @@ interface ServerAddressResolverMixin {
|
|||||||
@Redirect(
|
@Redirect(
|
||||||
method = {
|
method = {
|
||||||
"method_36903",
|
"method_36903",
|
||||||
"*(Lnet/minecraft/client/multiplayer/resolver/ServerAddress;)Ljava/util/Optional;"
|
"lambda$static$0"
|
||||||
},
|
},
|
||||||
at = @At(
|
at = @At(
|
||||||
value = "INVOKE",
|
value = "NEW",
|
||||||
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"
|
target = "(Ljava/net/InetAddress;I)Ljava/net/InetSocketAddress;"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
private static InetAddress eliminateRDNS(final String name) throws UnknownHostException {
|
private static InetSocketAddress eliminateRDNS(InetAddress addr, final int port,
|
||||||
final InetAddress ret = InetAddress.getByName(name);
|
@Local(ordinal = 0, argsOnly = true) final ServerAddress serverAddress) throws UnknownHostException {
|
||||||
|
final byte[] address = addr.getAddress();
|
||||||
final byte[] address = ret.getAddress();
|
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
// pass name to prevent rDNS
|
// pass name to prevent rDNS
|
||||||
return InetAddress.getByAddress(name, address);
|
addr = InetAddress.getByAddress(serverAddress.getHost(), address);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return new InetSocketAddress(addr, port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1042,7 +1042,7 @@ public final class MoonriseRegionFileIO {
|
|||||||
LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr);
|
LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compoundTag == null) {
|
if (throwable == null && compoundTag == null) {
|
||||||
// need to re-try from the start
|
// need to re-try from the start
|
||||||
this.scheduleReadIO();
|
this.scheduleReadIO();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import java.util.function.Function;
|
|||||||
public final class RegionizedPlayerChunkLoader {
|
public final class RegionizedPlayerChunkLoader {
|
||||||
|
|
||||||
public static final TicketType<Long> PLAYER_TICKET = TicketType.create("chunk_system:player_ticket", Long::compareTo);
|
public static final TicketType<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 MIN_VIEW_DISTANCE = 2;
|
||||||
public static final int MAX_VIEW_DISTANCE = 32;
|
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 LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
|
||||||
public static final int TICK_TICKET_LEVEL = ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL;
|
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 {
|
public static final class ViewDistanceHolder {
|
||||||
|
|
||||||
private volatile ViewDistances viewDistances;
|
private volatile ViewDistances viewDistances;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
||||||
|
|
||||||
|
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
||||||
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
||||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||||
import ca.spottedleaf.concurrentutil.util.Priority;
|
import ca.spottedleaf.concurrentutil.util.Priority;
|
||||||
@@ -82,6 +83,7 @@ public final class ChunkHolderManager {
|
|||||||
private long currentTick;
|
private long currentTick;
|
||||||
|
|
||||||
private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>();
|
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) -> {
|
private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
|
||||||
if (c1 == c2) {
|
if (c1 == c2) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -110,20 +112,20 @@ public final class ChunkHolderManager {
|
|||||||
this.unloadQueue = new ChunkUnloadQueue(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift());
|
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 ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
|
||||||
final int ticketMask = (1 << ticketShift) - 1;
|
final int ticketMask = (1 << ticketShift) - 1;
|
||||||
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
|
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
|
||||||
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
|
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
|
||||||
final boolean ret;
|
final boolean ret;
|
||||||
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
|
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
|
||||||
((posX >> ticketShift) - 1) << ticketShift,
|
((chunkX >> ticketShift) - 1) << ticketShift,
|
||||||
((posZ >> ticketShift) - 1) << ticketShift,
|
((chunkZ >> ticketShift) - 1) << ticketShift,
|
||||||
(((posX >> ticketShift) + 1) << ticketShift) | ticketMask,
|
(((chunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
|
||||||
(((posZ >> ticketShift) + 1) << ticketShift) | ticketMask
|
(((chunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
ret = this.processTicketUpdatesNoLock(posX >> ticketShift, posZ >> ticketShift, scheduledTasks, changedFullStatus);
|
ret = this.processTicketUpdatesNoLock(chunkX >> ticketShift, chunkZ >> ticketShift, scheduledTasks, changedFullStatus);
|
||||||
} finally {
|
} finally {
|
||||||
this.ticketLockArea.unlock(ticketLock);
|
this.ticketLockArea.unlock(ticketLock);
|
||||||
}
|
}
|
||||||
@@ -219,6 +221,8 @@ public final class ChunkHolderManager {
|
|||||||
LOGGER.error("Failed to close '" + type.name() + "' regionfile cache for world '" + WorldUtil.getWorldName(this.world) + "'", ex);
|
LOGGER.error("Failed to close '" + type.name() + "' regionfile cache for world '" + WorldUtil.getWorldName(this.world) + "'", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.taskScheduler.setShutdown(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensureInAutosave(final NewChunkHolder holder) {
|
void ensureInAutosave(final NewChunkHolder holder) {
|
||||||
@@ -719,6 +723,9 @@ public final class ChunkHolderManager {
|
|||||||
return removeDelay <= 0L;
|
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();) {
|
for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) {
|
||||||
final long sectionKey = iterator.nextLong();
|
final long sectionKey = iterator.nextLong();
|
||||||
|
|
||||||
@@ -727,9 +734,16 @@ public final class ChunkHolderManager {
|
|||||||
continue;
|
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(
|
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
|
||||||
CoordinateUtils.getChunkX(sectionKey) << sectionShift,
|
((lowerChunkX >> ticketShift) - 1) << ticketShift,
|
||||||
CoordinateUtils.getChunkZ(sectionKey) << sectionShift
|
((lowerChunkZ >> ticketShift) - 1) << ticketShift,
|
||||||
|
(((lowerChunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
|
||||||
|
(((lowerChunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -776,9 +790,23 @@ public final class ChunkHolderManager {
|
|||||||
if (chunkToExpireCount.isEmpty()) {
|
if (chunkToExpireCount.isEmpty()) {
|
||||||
this.sectionToChunkToExpireCount.remove(sectionKey);
|
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 {
|
} finally {
|
||||||
this.ticketLockArea.unlock(ticketLock);
|
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();
|
this.processTicketUpdates();
|
||||||
@@ -1005,14 +1033,9 @@ public final class ChunkHolderManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!TickThread.isTickThread()) {
|
if (!TickThread.isTickThread()) {
|
||||||
this.taskScheduler.scheduleChunkTask(() -> {
|
// These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
|
||||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
|
// which will invoke processTicketUpdates
|
||||||
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus);
|
||||||
pendingFullLoadUpdate.add(changedFullStatus.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
ChunkHolderManager.this.processPendingFullUpdate();
|
|
||||||
}, Priority.HIGHEST);
|
|
||||||
} else {
|
} else {
|
||||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
||||||
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
||||||
@@ -1293,36 +1316,20 @@ public final class ChunkHolderManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean processTicketUpdates() {
|
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) {
|
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
||||||
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
|
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");
|
TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<NewChunkHolder> changedFullStatus = null;
|
|
||||||
|
|
||||||
final boolean isTickThread = TickThread.isTickThread();
|
|
||||||
|
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
|
|
||||||
final boolean canProcessScheduling = scheduledTasks == null;
|
|
||||||
|
|
||||||
if (this.ticketLevelPropagator.hasPendingUpdates()) {
|
if (this.ticketLevelPropagator.hasPendingUpdates()) {
|
||||||
if (scheduledTasks == null) {
|
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
|
||||||
scheduledTasks = new ArrayList<>();
|
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
|
||||||
}
|
|
||||||
changedFullStatus = new ArrayList<>();
|
|
||||||
|
|
||||||
this.blockTicketUpdates();
|
this.blockTicketUpdates();
|
||||||
try {
|
try {
|
||||||
@@ -1333,27 +1340,42 @@ public final class ChunkHolderManager {
|
|||||||
} finally {
|
} finally {
|
||||||
this.unblockTicketUpdates(Boolean.FALSE);
|
this.unblockTicketUpdates(Boolean.FALSE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (changedFullStatus != null) {
|
|
||||||
this.addChangedStatuses(changedFullStatus);
|
this.addChangedStatuses(changedFullStatus);
|
||||||
}
|
|
||||||
|
|
||||||
if (canProcessScheduling && scheduledTasks != null) {
|
|
||||||
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
|
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
|
||||||
scheduledTasks.get(i).schedule();
|
scheduledTasks.get(i).schedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canProcessFullUpdates) {
|
if (isTickThread) {
|
||||||
ret |= this.processPendingFullUpdate();
|
ret |= this.processPendingFullUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
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
|
// only call on tick thread
|
||||||
private boolean processPendingFullUpdate() {
|
private boolean processPendingFullUpdate() {
|
||||||
|
this.processOffThreadFullUpdates();
|
||||||
|
|
||||||
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
||||||
|
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
|
|||||||
@@ -271,6 +271,16 @@ public final class ChunkTaskScheduler {
|
|||||||
return this.lockShift;
|
return this.lockShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private volatile boolean shutdown;
|
||||||
|
|
||||||
|
public boolean hasShutdown() {
|
||||||
|
return this.shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShutdown(final boolean shutdown) {
|
||||||
|
this.shutdown = shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
public ChunkTaskScheduler(final ServerLevel world) {
|
public ChunkTaskScheduler(final ServerLevel world) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
// must be >= region shift (in paper, doesn't exist) and must be >= ticket propagator section shift
|
// must be >= region shift (in paper, doesn't exist) and must be >= ticket propagator section shift
|
||||||
@@ -524,6 +534,13 @@ public final class ChunkTaskScheduler {
|
|||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.hasShutdown()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Chunk system has shut down, cannot process chunk requests in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(this.world) + "' at "
|
||||||
|
+ "(" + chunkX + "," + chunkZ + ") status: " + status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final Long ticketId = getNextNonFullLoadId();
|
final Long ticketId = getNextNonFullLoadId();
|
||||||
final int ticketLevel = getTicketLevel(status);
|
final int ticketLevel = getTicketLevel(status);
|
||||||
this.chunkHolderManager.addTicketAtLevel(NON_FULL_CHUNK_LOAD, chunkX, chunkZ, ticketLevel, ticketId);
|
this.chunkHolderManager.addTicketAtLevel(NON_FULL_CHUNK_LOAD, chunkX, chunkZ, ticketLevel, ticketId);
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ accessible class net/minecraft/server/level/DistanceManager$FixedPlayerDistanceC
|
|||||||
# Ticket
|
# Ticket
|
||||||
accessible field net/minecraft/server/level/Ticket key Ljava/lang/Object;
|
accessible field net/minecraft/server/level/Ticket key Ljava/lang/Object;
|
||||||
accessible field net/minecraft/server/level/TicketType timeout J
|
accessible field net/minecraft/server/level/TicketType timeout J
|
||||||
|
mutable field net/minecraft/server/level/TicketType timeout J
|
||||||
|
|
||||||
|
|
||||||
# ServerChunkCache
|
# ServerChunkCache
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"chunk_system.ChunkStepMixin",
|
"chunk_system.ChunkStepMixin",
|
||||||
"chunk_system.ChunkStorageMixin",
|
"chunk_system.ChunkStorageMixin",
|
||||||
"chunk_system.DistanceManagerMixin",
|
"chunk_system.DistanceManagerMixin",
|
||||||
|
"chunk_system.DynamicGameEventListenerMixin",
|
||||||
"chunk_system.EntityGetterMixin",
|
"chunk_system.EntityGetterMixin",
|
||||||
"chunk_system.EntityMixin",
|
"chunk_system.EntityMixin",
|
||||||
"chunk_system.EntityTickListMixin",
|
"chunk_system.EntityTickListMixin",
|
||||||
|
|||||||
Reference in New Issue
Block a user