diff --git a/build.gradle.kts b/build.gradle.kts
index f03cb43..d9849f6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
plugins {
- id("com.falsepattern.fpgradle-mc") version("0.18.0")
+ id("com.falsepattern.fpgradle-mc") version("2.1.0")
}
group = "com.falsepattern"
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 1b33c55..8bdaf60 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ff23a68..2e11132 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 23d15a9..adff685 100644
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
#!/bin/sh
#
-# Copyright © 2015-2021 the original authors.
+# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
diff --git a/gradlew.bat b/gradlew.bat
index db3a6ac..c4bdd3a 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -70,11 +70,10 @@ goto fail
:execute
@rem Setup the command line
-set CLASSPATH=
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/src/main/java/com/falsepattern/chunk/api/DataRegistry.java b/src/main/java/com/falsepattern/chunk/api/DataRegistry.java
index e4a6e8a..3527348 100644
--- a/src/main/java/com/falsepattern/chunk/api/DataRegistry.java
+++ b/src/main/java/com/falsepattern/chunk/api/DataRegistry.java
@@ -31,6 +31,7 @@
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.util.Set;
+import java.util.SortedSet;
/**
* This is an API class covered by the additional permissions in the license.
@@ -48,12 +49,36 @@ public class DataRegistry {
* Registers a ChunkDataManager. Only do this during the init phase.
*
* @param manager The manager to register.
+ * @param ordering The natural ordering index for the data manager when iterating the list of managers.
+ *
+ * ChunkAPI, Lumi, RPLE, and EndlessIDs all use 0. Negative numbers are sorted earlier. Positive numbers are sorted later.
+ *
+ * Use 0 unless you specifically need to do something later.
+ *
+ * As a convention, do this in increments of 1000 so that other people can order "between" your manager and the base managers.
+ *
*
* @throws IllegalStateException If the registration stage is over.
* @throws IllegalArgumentException If the manager has a duplicate id.
*/
+ public static void registerDataManager(DataManager manager, int ordering) throws IllegalStateException, IllegalArgumentException {
+ DataRegistryImpl.registerDataManager(manager, ordering);
+ }
+
+ /**
+ * Registers a ChunkDataManager. Only do this during the init phase.
+ * Has an implicit ordering index of 0.
+ *
+ * @deprecated Use {@link #registerDataManager(DataManager, int)} with an explicit ordering.
+ *
+ * @param manager The manager to register.
+ *
+ * @throws IllegalStateException If the registration stage is over.
+ * @throws IllegalArgumentException If the manager has a duplicate id.
+ */
+ @Deprecated
public static void registerDataManager(DataManager manager) throws IllegalStateException, IllegalArgumentException {
- DataRegistryImpl.registerDataManager(manager);
+ DataRegistryImpl.registerDataManager(manager, 0);
}
/**
@@ -72,10 +97,22 @@ public static void disableDataManager(String domain, String id) throws IllegalSt
* The id of a manager is its domain and id separated by a colon. (domain:id)
*/
@Contract(pure = true)
+ @Deprecated
public static @Unmodifiable Set getRegisteredManagers() {
return DataRegistryImpl.getRegisteredManagers();
}
+ /**
+ * Returns an unmodifiable set of all registered ChunkDataManagers.
+ * The id of a manager is its domain and id separated by a colon. (domain:id)
+ *
+ * This function also includes the ordering index of each manager.
+ */
+ @Contract(pure = true)
+ public static @Unmodifiable SortedSet getRegisteredManagersOrdered() {
+ return DataRegistryImpl.getRegisteredManagersOrdered();
+ }
+
/**
* Copies chunk-level data from a source chunk to a target chunk.
* DOES NOT copy data contained inside its ExtendedBlockStorage instances!!
diff --git a/src/main/java/com/falsepattern/chunk/api/OrderedManager.java b/src/main/java/com/falsepattern/chunk/api/OrderedManager.java
new file mode 100644
index 0000000..6da7fc0
--- /dev/null
+++ b/src/main/java/com/falsepattern/chunk/api/OrderedManager.java
@@ -0,0 +1,55 @@
+/*
+ * ChunkAPI
+ *
+ * Copyright (C) 2023-2025 FalsePattern, The MEGA Team, LegacyModdingMC contributors
+ * All Rights Reserved
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, only version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.falsepattern.chunk.api;
+
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+@RequiredArgsConstructor
+public class OrderedManager implements Comparable {
+ public final int ordering;
+ public final @NotNull String id;
+ @Override
+ public int compareTo(OrderedManager o) {
+ int ord = Integer.compare(ordering, o.ordering);
+ if (ord != 0)
+ return ord;
+ return id.compareTo(o.id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof OrderedManager))
+ return false;
+ val other = (OrderedManager) obj;
+ return id.equals(other.id) && ordering == other.ordering;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ordering, id);
+ }
+}
diff --git a/src/main/java/com/falsepattern/chunk/internal/ChunkAPI.java b/src/main/java/com/falsepattern/chunk/internal/ChunkAPI.java
index e29f818..81393d6 100644
--- a/src/main/java/com/falsepattern/chunk/internal/ChunkAPI.java
+++ b/src/main/java/com/falsepattern/chunk/internal/ChunkAPI.java
@@ -41,11 +41,11 @@ public class ChunkAPI {
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
- DataRegistry.registerDataManager(new BlockIDManager());
- DataRegistry.registerDataManager(new MetadataManager());
- DataRegistry.registerDataManager(new LightingManager());
- DataRegistry.registerDataManager(new BlocklightManager());
- DataRegistry.registerDataManager(new SkylightManager());
- DataRegistry.registerDataManager(new BiomeManager());
+ DataRegistry.registerDataManager(new BlockIDManager(), 0);
+ DataRegistry.registerDataManager(new MetadataManager(), 0);
+ DataRegistry.registerDataManager(new LightingManager(), 0);
+ DataRegistry.registerDataManager(new BlocklightManager(), 0);
+ DataRegistry.registerDataManager(new SkylightManager(), 0);
+ DataRegistry.registerDataManager(new BiomeManager(), 0);
}
}
\ No newline at end of file
diff --git a/src/main/java/com/falsepattern/chunk/internal/DataRegistryImpl.java b/src/main/java/com/falsepattern/chunk/internal/DataRegistryImpl.java
index 5b6645a..936bfcc 100644
--- a/src/main/java/com/falsepattern/chunk/internal/DataRegistryImpl.java
+++ b/src/main/java/com/falsepattern/chunk/internal/DataRegistryImpl.java
@@ -23,6 +23,7 @@
package com.falsepattern.chunk.internal;
import com.falsepattern.chunk.api.DataManager;
+import com.falsepattern.chunk.api.OrderedManager;
import lombok.Data;
import lombok.val;
import lombok.var;
@@ -48,15 +49,20 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.stream.Collectors;
public class DataRegistryImpl {
- private static final Set managers = new HashSet<>();
- private static final Map packetManagers = new HashMap<>();
- private static final Map blockPacketManagers = new HashMap<>();
- private static final Map NBTManagers = new HashMap<>();
- private static final Map chunkNBTManagers = new HashMap<>();
- private static final Map subChunkNBTManagers = new HashMap<>();
+ private static final Map managersUnordered = new HashMap<>();
+ private static final SortedSet managers = new TreeSet<>();
+ private static final DualMap packetManagers = new DualMap<>();
+ private static final DualMap blockPacketManagers = new DualMap<>();
+ private static final DualMap NBTManagers = new DualMap<>();
+ private static final SortedMap chunkNBTManagers = new TreeMap<>();
+ private static final SortedMap subChunkNBTManagers = new TreeMap<>();
private static final Set disabledManagers = new HashSet<>();
private static int maxPacketSize = 4;
@@ -66,12 +72,12 @@ private static class PacketManagerInfo {
public final DataManager.PacketDataManager manager;
}
- public static void registerDataManager(DataManager manager) throws IllegalStateException, IllegalArgumentException {
+ public static void registerDataManager(DataManager manager, int ordering) throws IllegalStateException, IllegalArgumentException {
if (Loader.instance().getLoaderState() != LoaderState.INITIALIZATION) {
throw new IllegalStateException("ChunkDataManager registration is not allowed at this time! Please register your ChunkDataManager in the init phase.");
}
var id = manager.domain() + ":" + manager.id();
- if (managers.contains(id)) {
+ if (managersUnordered.containsKey(id)) {
throw new IllegalArgumentException("ChunkDataManager " + manager + " has a duplicate id!");
}
@@ -80,24 +86,27 @@ public static void registerDataManager(DataManager manager) throws IllegalStateE
return;
}
- managers.add(id);
+ val ord = new OrderedManager(ordering, id);
+ managersUnordered.put(id, ord);
+ managers.add(ord);
if (manager instanceof DataManager.PacketDataManager) {
val packetManager = (DataManager.PacketDataManager) manager;
val maxSize = packetManager.maxPacketSize();
maxPacketSize += 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + maxSize;
- packetManagers.put(id, new PacketManagerInfo(maxSize, packetManager));
+ val man = new PacketManagerInfo(maxSize, packetManager);
+ packetManagers.put(ord, man);
}
if (manager instanceof DataManager.BlockPacketDataManager) {
val blockPacketManager = (DataManager.BlockPacketDataManager) manager;
- blockPacketManagers.put(id, blockPacketManager);
+ blockPacketManagers.put(ord, blockPacketManager);
}
if (manager instanceof DataManager.StorageDataManager) {
- NBTManagers.put(id, (DataManager.StorageDataManager) manager);
+ NBTManagers.put(ord, (DataManager.StorageDataManager) manager);
if (manager instanceof DataManager.ChunkDataManager) {
- chunkNBTManagers.put(id, (DataManager.ChunkDataManager) manager);
+ chunkNBTManagers.put(ord, (DataManager.ChunkDataManager) manager);
}
if (manager instanceof DataManager.SubChunkDataManager) {
- subChunkNBTManagers.put(id, (DataManager.SubChunkDataManager) manager);
+ subChunkNBTManagers.put(ord, (DataManager.SubChunkDataManager) manager);
}
}
}
@@ -109,17 +118,19 @@ public static void disableDataManager(String domain, String id) {
Common.LOG.debug("Disabling ChunkDataManager " + id + " in domain " + domain + ". See the stacktrace for the source of this event.\nThis is NOT an error.",
new Throwable());
val manager = domain + ":" + id;
+ val ord = managersUnordered.remove(manager);
//Remove the manager from the list of managers, if it exists
- if (managers.remove(manager)) {
+ if (ord != null) {
+ managers.remove(ord);
//Clear the maps
- if (packetManagers.containsKey(manager)) {
- val removed = packetManagers.remove(manager);
+ if (packetManagers.containsKey(ord)) {
+ val removed = packetManagers.remove(ord);
maxPacketSize -= 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + removed.maxPacketSize;
}
- blockPacketManagers.remove(manager);
- chunkNBTManagers.remove(manager);
- subChunkNBTManagers.remove(manager);
- NBTManagers.remove(manager);
+ blockPacketManagers.remove(ord);
+ chunkNBTManagers.remove(ord);
+ subChunkNBTManagers.remove(ord);
+ NBTManagers.remove(ord);
}
//Add the manager to the list of disabled managers, in case it gets registered after this disable call.
@@ -171,9 +182,9 @@ public static int writeToBuffer(Chunk chunk, int subChunkMask, boolean forceUpda
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putInt(packetManagers.size());
for (val pair : packetManagers.entrySet()) {
- val id = pair.getKey();
+ val ord = pair.getKey();
val managerInfo = pair.getValue();
- writeString(buf, id);
+ writeString(buf, ord.id);
int start = buf.position() + 4;
val slice = createSlice(buf, start, managerInfo.maxPacketSize);
managerInfo.manager.writeToBuffer(chunk, subChunkMask, forceUpdate, slice);
@@ -199,9 +210,9 @@ public static void readBlockFromPacket(Chunk chunk, int x, int y, int z, S23Pack
public static void writeBlockPacketToBuffer(S23PacketBlockChange packet, PacketBuffer buffer) throws IOException {
buffer.writeInt(blockPacketManagers.size());
for (val pair : blockPacketManagers.entrySet()) {
- val id = pair.getKey();
+ val ord = pair.getKey();
val manager = pair.getValue();
- buffer.writeStringToBuffer(id);
+ buffer.writeStringToBuffer(ord.id);
manager.writeBlockPacketToBuffer(packet, buffer);
}
}
@@ -300,7 +311,11 @@ public static void cloneSubChunk(Chunk fromChunk, ExtendedBlockStorage from, Ext
}
public static Set getRegisteredManagers() {
- return Collections.unmodifiableSet(managers);
+ return Collections.unmodifiableSet(managersUnordered.keySet());
+ }
+
+ public static SortedSet getRegisteredManagersOrdered() {
+ return Collections.unmodifiableSortedSet(managers);
}
public static void readLevelDat(NBTTagCompound tag) {
@@ -349,7 +364,9 @@ private static String verifyManagerCompatibility(NBTTagCompound tag) {
StringBuilder builder = new StringBuilder();
val saveManagers = readManagers(tag);
val removedManagers = new HashSet<>(saveManagers.keySet());
- removedManagers.removeAll(NBTManagers.keySet());
+ for (val nbtManager: NBTManagers.keySet()) {
+ removedManagers.remove(nbtManager.id);
+ }
if (!removedManagers.isEmpty()) {
compatWarning = true;
builder.append("\nThe following data managers are no longer present:\n");
@@ -370,8 +387,8 @@ private static String verifyManagerCompatibility(NBTTagCompound tag) {
}
val addedManagers = NBTManagers.keySet()
.stream()
- .filter(manager -> !manager.startsWith("minecraft:"))
- .filter(manager -> !saveManagers.containsKey(manager))
+ .filter(manager -> !manager.id.startsWith("minecraft:"))
+ .filter(manager -> !saveManagers.containsKey(manager.id))
.collect(Collectors.toSet());
if (!addedManagers.isEmpty()) {
compatWarning = true;
@@ -417,12 +434,12 @@ public static void writeLevelDat(NBTTagCompound tag) {
tag.setTag("managers", managers);
tag.setString("version", Tags.MOD_VERSION);
for (val manager : NBTManagers.entrySet()) {
- val name = manager.getKey();
- if (name.startsWith("minecraft:")) {
+ val ord = manager.getKey();
+ if (ord.id.startsWith("minecraft:")) {
continue;
}
val value = manager.getValue();
- managers.setTag(name, SaveManagerInfo.fromManager(value).toNBT());
+ managers.setTag(ord.id, SaveManagerInfo.fromManager(value).toNBT());
}
}
@@ -435,7 +452,6 @@ private static Map readManagers(NBTTagCompound tag) {
return managers;
}
val managerTag = tag.getCompoundTag("managers");
- //noinspection unchecked
for (val key : managerTag.func_150296_c()) {
managers.put(key, SaveManagerInfo.fromNBT(managerTag.getCompoundTag(key)));
}
diff --git a/src/main/java/com/falsepattern/chunk/internal/DualMap.java b/src/main/java/com/falsepattern/chunk/internal/DualMap.java
new file mode 100644
index 0000000..16cd4cb
--- /dev/null
+++ b/src/main/java/com/falsepattern/chunk/internal/DualMap.java
@@ -0,0 +1,86 @@
+/*
+ * ChunkAPI
+ *
+ * Copyright (C) 2023-2025 FalsePattern, The MEGA Team, LegacyModdingMC contributors
+ * All Rights Reserved
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, only version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.falsepattern.chunk.internal;
+
+import com.falsepattern.chunk.api.OrderedManager;
+import lombok.val;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class DualMap {
+ private final Map unordered = new HashMap<>();
+ private final SortedMap ordered = new TreeMap<>();
+
+ public void put(OrderedManager man, T value) {
+ val a = unordered.put(man.id, value);
+ val b = ordered.put(man, value);
+ if (a != b) {
+ throw new AssertionError();
+ }
+ }
+
+ public int size() {
+ return unordered.size();
+ }
+
+ public T get(OrderedManager ord) {
+ return unordered.get(ord.id);
+ }
+
+ public T get(String id) {
+ return unordered.get(id);
+ }
+
+ public T remove(OrderedManager ord) {
+ val a = unordered.remove(ord.id);
+ val b = ordered.remove(ord);
+ if (a != b) {
+ throw new AssertionError();
+ }
+ return b;
+ }
+
+ public boolean containsKey(String id) {
+ return unordered.containsKey(id);
+ }
+
+ public boolean containsKey(OrderedManager ord) {
+ return unordered.containsKey(ord.id);
+ }
+
+ public Set keySet() {
+ return ordered.keySet();
+ }
+
+ public Set> entrySet() {
+ return ordered.entrySet();
+ }
+
+ public Iterable values() {
+ return ordered.values();
+ }
+}