29 Commits
Author SHA1 Message Date
FalsePattern ee3b8d2faa ci: update gradle and buildscript 2026-08-09 15:30:43 +02:00
FalsePattern a8ebbb530c ci: update gradle and buildscript 2026-08-09 15:30:11 +02:00
FalsePattern a05638f671 Merge commit 'e68cbb570bcd7b344009292dc4455f4418c4bb1d' into dev 2026-07-27 22:26:54 +02:00
RecursivePineapple e68cbb570b Fix NPE in SkylightManager 2026-07-25 10:18:06 -04:00
FalsePattern 7b885a5f12 docs: add NEID compatibility notice to readme 2026-05-21 23:52:51 +02:00
FalsePattern 18f7d15f3a Merge remote-tracking branch 'GTNewHorizons/fix-backup' into dev 2026-05-21 23:43:57 +02:00
FalsePattern 039017a286 Merge remote-tracking branch 'iocmet/patch-1' into dev 2026-05-21 23:42:55 +02:00
boubou19 40d27c7ee6 Do not ignore the fml.doNotBackup property 2026-05-21 15:48:32 +02:00
FalsePattern d7b4afe9c3 docs: update readme 2026-05-17 15:54:52 +02:00
IOcmet 1ba33b64a0 Use a more reliable Thermos check
Signed-off-by: IOcmet <63492966+iocmet@users.noreply.github.com>
2026-04-29 05:44:25 +03:00
FalsePattern a3165a0cca fix: crash when joining server due to buggy thermos compat patch 2026-03-07 16:11:32 +01:00
rjnasers 3c4ffe502c Actually fiixng it 2026-03-07 15:48:06 +01:00
rjnasers 68f297faba Merge branch 'master' of https://github.com/Cardinalstars/ChunkAPI-Cardinalstar 2026-03-07 15:22:21 +01:00
rjnasers 02f2321339 Fixing really stupid mistake I made 2026-03-07 15:22:15 +01:00
FalsePattern 220755b3f3 feat: Thermos compat
Closes: https://github.com/LegacyModdingMC/ChunkAPI/pull/22
2026-02-06 20:54:23 +01:00
rjnasers 5be9cb6ad7 Formatting 2026-01-27 16:15:10 +01:00
rjnasers 18e590236b Separating mixin implementations to be more DRY 2026-01-27 16:13:36 +01:00
rjnasers c40cebaa90 copyright 2026-01-27 15:44:34 +01:00
rjnasers 7efe0cbcf4 Some cleanup 2026-01-27 14:00:20 +01:00
rjnasers d00d40e0e4 A start 2026-01-24 01:18:05 +01:00
FalsePattern 01de5f589a Merge remote-tracking branch 'pr18/cc-compat' 2026-01-23 20:04:36 +01:00
RecursivePineapple 023d9c96ee Cubic Chunks Compatibility 2025-11-26 11:06:09 -05:00
FalsePattern b94617d502 feat: Spool compat 2025-10-16 15:35:27 +02:00
FalsePattern 66365292a4 docs: Remove fplib dep in readme 2025-10-16 15:32:26 +02:00
FalsePattern 98cb70ab42 chore: Port to V2 mixins 2025-10-16 15:32:17 +02:00
FalsePattern 3da2da2846 build: Update foojay resolver 2025-10-16 15:18:15 +02:00
FalsePattern f1a2c011e2 build: Add gradle caching 2025-10-16 15:17:42 +02:00
FalsePattern bd9a1d1f81 build: Update gradle and buildscript 2025-09-27 20:54:17 +02:00
FalsePattern 5d44d49968 feat: Ordered data managers 2025-09-27 20:53:20 +02:00
42 changed files with 974 additions and 630 deletions

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ permissions:
jobs:
release-tags:
uses: FalsePattern/fpgradle-workflows/.github/workflows/release-tags.yml@master
uses: FalsePattern/fpgradle-workflows/.github/workflows/release-tags-2.yml@master
with:
workspace: "setupCIWorkspace"
secrets:
+2 -2
View File
@@ -1,5 +1,5 @@
plugins {
id("com.falsepattern.fpgradle-mc") version("0.18.0")
id("com.falsepattern.fpgradle-mc") version("4.1.0")
}
group = "com.falsepattern"
@@ -17,7 +17,6 @@ minecraft_fp {
mixin {
pkg = "internal.mixin.mixins"
pluginClass = "internal.mixin.plugin.MixinPlugin"
}
core {
@@ -37,6 +36,7 @@ minecraft_fp {
}
curseforge {
projectId = "844484"
side = Both
}
modrinth {
projectId = "y0vBUOla"
+2
View File
@@ -0,0 +1,2 @@
org.gradle.caching=true
org.gradle.configuration-cache=true
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
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.7.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+4 -7
View File
@@ -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.
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -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" \
"$@"
Vendored
+12 -24
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,30 +65,18 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
: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" %*
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+19 -1
View File
@@ -11,6 +11,15 @@ loading hooks, and more.
### Mod logo by Houstonruss
NotEnoughIDs Compatibility Notice
-----
This mod is NOT compatible with NotEnoughIDs (both the original and the GTNH variant). use [EndlessIDs](https://github.com/GTMEGA/EndlessIDs) instead.
**BACKUP YOUR SAVE FIRST!**
If you only need ID extension on block/item IDs, you can turn off the extensions of other ID types in EndlessIDs to prevent compatibility issues!
Goals
-----
@@ -112,10 +121,19 @@ Manager data:
| 4 | int | (m) Manager data length |
| m | byte[m] | Manager data |
## Strict No LLM / No AI Policy
No LLMs for issues.
No LLMs for patches / pull requests.
No LLMs for comments on the bug tracker, including translation.
English is encouraged, but not required. You are welcome to post in your native
language and rely on others to have their own translation tools of choice to
interpret your words.
Dependencies
------------
[FalsePatternLib](https://github.com/Falsepattern/FalsePatternLib)
[UniMixins](https://github.com/LegacyModdingMC/UniMixins)
+1 -1
View File
@@ -1,5 +1,5 @@
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version("0.9.0")
id("org.gradle.toolchains.foojay-resolver-convention") version("1.0.0")
}
rootProject.name = "ChunkAPI"
@@ -22,6 +22,7 @@
package com.falsepattern.chunk.api;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -84,6 +85,9 @@ public interface DataManager {
* Serializes your data into a packet.
*
* @param chunk The chunk to serialize.
* @param subChunkMask The mask that controls which subchunks need to be serialized. This will be 0 when cubic chunks is
* present - only chunk-specific information (like biomes) should be sent in this case.
* @param forceUpdate True when the chunk is first synced.
*/
@Contract(mutates = "param4")
void writeToBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, ByteBuffer buffer);
@@ -91,13 +95,55 @@ public interface DataManager {
/**
* Deserializes your data from a packet.
*
* @param chunk The chunk to deserialize.
* @param buffer The packet buffer to read from.
* @param chunk The chunk to deserialize.
* @param subChunkMask The mask that controls which subchunks need to be deserialized. This will be 0 when cubic chunks is
* present - only chunk-specific information (like biomes) should be sent in this case.
* @param forceUpdate True when the chunk is first synced.
*/
@Contract(mutates = "param1,param4")
void readFromBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, ByteBuffer buffer);
}
/**
* A cube-specific variant of {@link PacketDataManager}. This is only used by cubic chunks.
*
* @author RecursivePineapple
* @since 0.7.1
* @apiNote Cubes, subchunks, and ExtendedBlockStorages are equivalent. A cube is a subchunk and a cube contains an EBS.
* To keep dependencies simple, cube-specific information is not available to this interface, but a cube's location can be
* retrieved by combining the chunk location and the EBS Y level. Note that the EBS's Y level can be <0 and >= 16.
*/
@ApiStatus.Experimental
interface CubicPacketDataManager extends DataManager {
/**
* @return The maximum amount of bytes your data can take up in a packet.
*
* @implSpec This is used to determine the size of the packet compression/decompression buffer.
* Only called ONCE, during registration!
*/
@Contract(pure = true)
int maxPacketSizeCubic();
/**
* Serializes your data into a packet.
*
* @param chunk The chunk that contains the subchunk.
* @param blockStorage The subchunk (cube) that was updated.
*/
@Contract(mutates = "param3")
void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer);
/**
* Deserializes your data from a packet.
*
* @param chunk The chunk that contains the subchunk.
* @param blockStorage The subchunk (cube) that was updated.
*/
@Contract(mutates = "param1,param2,param3")
void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer);
}
/**
* Implement this interface if you additionally want to synchronize your data on single and multi-block updates,
* not just chunk updates.
@@ -31,6 +31,7 @@ import net.minecraft.world.chunk.Chunk;
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.
* <p>
* ChunkAPI, Lumi, RPLE, and EndlessIDs all use 0. Negative numbers are sorted earlier. Positive numbers are sorted later.
* <p>
* Use 0 unless you specifically need to do something later.
* <p>
* 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 class DataRegistry {
* The id of a manager is its domain and id separated by a colon. (domain:id)
*/
@Contract(pure = true)
@Deprecated
public static @Unmodifiable Set<String> 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)
* <p>
* This function also includes the ordering index of each manager.
*/
@Contract(pure = true)
public static @Unmodifiable SortedSet<OrderedManager> 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!!
@@ -20,26 +20,36 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin;
package com.falsepattern.chunk.api;
import com.falsepattern.chunk.internal.Tags;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.IMixin;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.IMixinPlugin;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.ITargetedMod;
import lombok.Getter;
import org.apache.logging.log4j.Logger;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.jetbrains.annotations.NotNull;
public class MixinPlugin implements IMixinPlugin {
@Getter
private final Logger logger = IMixinPlugin.createLogger(Tags.MOD_NAME);
import java.util.Objects;
@RequiredArgsConstructor
public class OrderedManager implements Comparable<OrderedManager> {
public final int ordering;
public final @NotNull String id;
@Override
public ITargetedMod[] getTargetedModEnumValues() {
return TargetedMod.values();
public int compareTo(OrderedManager o) {
int ord = Integer.compare(ordering, o.ordering);
if (ord != 0)
return ord;
return id.compareTo(o.id);
}
@Override
public IMixin[] getMixinEnumValues() {
return Mixin.values();
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);
}
}
@@ -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);
}
}
@@ -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,17 +49,24 @@ import java.util.HashMap;
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<String> managers = new HashSet<>();
private static final Map<String, PacketManagerInfo> packetManagers = new HashMap<>();
private static final Map<String, DataManager.BlockPacketDataManager> blockPacketManagers = new HashMap<>();
private static final Map<String, DataManager.StorageDataManager> NBTManagers = new HashMap<>();
private static final Map<String, DataManager.ChunkDataManager> chunkNBTManagers = new HashMap<>();
private static final Map<String, DataManager.SubChunkDataManager> subChunkNBTManagers = new HashMap<>();
private static final Map<String, OrderedManager> managersUnordered = new HashMap<>();
private static final SortedSet<OrderedManager> managers = new TreeSet<>();
private static final DualMap<PacketManagerInfo> packetManagers = new DualMap<>();
private static final DualMap<CubicPacketManagerInfo> cubicPacketManagers = new DualMap<>();
private static final DualMap<DataManager.BlockPacketDataManager> blockPacketManagers = new DualMap<>();
private static final DualMap<DataManager.StorageDataManager> NBTManagers = new DualMap<>();
private static final SortedMap<OrderedManager, DataManager.ChunkDataManager> chunkNBTManagers = new TreeMap<>();
private static final SortedMap<OrderedManager, DataManager.SubChunkDataManager> subChunkNBTManagers = new TreeMap<>();
private static final Set<String> disabledManagers = new HashSet<>();
private static int maxPacketSize = 4;
private static int maxPacketSizeCubic = 4;
@Data
private static class PacketManagerInfo {
@@ -66,12 +74,18 @@ public class DataRegistryImpl {
public final DataManager.PacketDataManager manager;
}
public static void registerDataManager(DataManager manager) throws IllegalStateException, IllegalArgumentException {
@Data
private static class CubicPacketManagerInfo {
public final int maxPacketSize;
public final DataManager.CubicPacketDataManager manager;
}
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 +94,33 @@ public class DataRegistryImpl {
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.CubicPacketDataManager) {
val cubicPacketManager = (DataManager.CubicPacketDataManager) manager;
val maxSize = cubicPacketManager.maxPacketSizeCubic();
maxPacketSizeCubic += 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + maxSize;
cubicPacketManagers.put(ord, new CubicPacketManagerInfo(maxSize, cubicPacketManager));
}
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 +132,23 @@ public class DataRegistryImpl {
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);
if (cubicPacketManagers.containsKey(ord)) {
val removed = cubicPacketManagers.remove(ord);
maxPacketSizeCubic -= 4 + id.getBytes(StandardCharsets.UTF_8).length + 4 + removed.maxPacketSize;
}
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.
@@ -130,6 +159,10 @@ public class DataRegistryImpl {
return maxPacketSize;
}
public static int maxPacketSizeCubic() {
return maxPacketSizeCubic;
}
private static void writeString(ByteBuffer buffer, String string) {
val bytes = string.getBytes();
buffer.putInt(bytes.length);
@@ -166,14 +199,37 @@ public class DataRegistryImpl {
}
}
public static void readFromBufferCubic(Chunk chunk, ExtendedBlockStorage blockStorage, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
int count = buf.getInt();
for (int i = 0; i < count; i++) {
val id = readString(buf);
val length = buf.getInt();
val managerInfo = cubicPacketManagers.get(id);
if (managerInfo == null) {
Common.LOG.error("Received data for unknown CubicPacketDataManager " + id + ". Skipping.");
buf.position(buf.position() + length);
continue;
}
if (length > managerInfo.maxPacketSize) {
Common.LOG.error("Received packet larger than max size for CubicPacketDataManager " + id + "! Continuing anyways, things might break!");
}
int start = buf.position();
val slice = createSlice(buf, start, length);
managerInfo.manager.readFromBuffer(chunk, blockStorage, slice);
buf.position(start + length);
}
}
public static int writeToBuffer(Chunk chunk, int subChunkMask, boolean forceUpdate, byte[] data) {
val buf = ByteBuffer.wrap(data);
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);
@@ -184,6 +240,24 @@ public class DataRegistryImpl {
return buf.position();
}
public static int writeToBufferCubic(Chunk chunk, ExtendedBlockStorage blockStorage, byte[] data) {
val buf = ByteBuffer.wrap(data);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putInt(cubicPacketManagers.size());
for (val pair : cubicPacketManagers.entrySet()) {
val ord = pair.getKey();
val managerInfo = pair.getValue();
writeString(buf, ord.id);
int start = buf.position() + 4;
val slice = createSlice(buf, start, managerInfo.maxPacketSize);
managerInfo.manager.writeToBuffer(chunk, blockStorage, slice);
int length = slice.position();
buf.putInt(length);
buf.position(start + length);
}
return buf.position();
}
public static void writeBlockToPacket(Chunk chunk, int x, int y, int z, S23PacketBlockChange packet) {
for (val manager : blockPacketManagers.values()) {
manager.writeBlockToPacket(chunk, x, y, z, packet);
@@ -199,9 +273,9 @@ public class DataRegistryImpl {
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 +374,11 @@ public class DataRegistryImpl {
}
public static Set<String> getRegisteredManagers() {
return Collections.unmodifiableSet(managers);
return Collections.unmodifiableSet(managersUnordered.keySet());
}
public static SortedSet<OrderedManager> getRegisteredManagersOrdered() {
return Collections.unmodifiableSortedSet(managers);
}
public static void readLevelDat(NBTTagCompound tag) {
@@ -335,11 +413,14 @@ public class DataRegistryImpl {
StartupQuery.abort();
}
try {
ZipperUtil.backupWorld();
} catch (IOException e) {
StartupQuery.notify("The world backup couldn't be created.\n\n" + e);
StartupQuery.abort();
String skip = System.getProperty("fml.doNotBackup");
if (skip == null || !"true".equals(skip)) {
try {
ZipperUtil.backupWorld();
} catch (IOException e) {
StartupQuery.notify("The world backup couldn't be created.\n\n" + e);
StartupQuery.abort();
}
}
}
}
@@ -349,7 +430,9 @@ public class DataRegistryImpl {
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 +453,8 @@ public class DataRegistryImpl {
}
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 +500,12 @@ public class DataRegistryImpl {
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 +518,6 @@ public class DataRegistryImpl {
return managers;
}
val managerTag = tag.getCompoundTag("managers");
//noinspection unchecked
for (val key : managerTag.func_150296_c()) {
managers.put(key, SaveManagerInfo.fromNBT(managerTag.getCompoundTag(key)));
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
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<T> {
private final Map<String, T> unordered = new HashMap<>();
private final SortedMap<OrderedManager, T> 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<OrderedManager> keySet() {
return ordered.keySet();
}
public Set<Map.Entry<OrderedManager, T>> entrySet() {
return ordered.entrySet();
}
public Iterable<T> values() {
return ordered.values();
}
}
@@ -23,13 +23,19 @@
package com.falsepattern.chunk.internal.core;
import com.falsepattern.chunk.internal.Tags;
import com.falsepattern.chunk.internal.mixin.plugin.Mixin;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@IFMLLoadingPlugin.TransformerExclusions(Tags.ROOT_PKG + ".internal.core")
public class CoreLoadingPlugin implements IFMLLoadingPlugin {
public class CoreLoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader {
@Override
public String[] getASMTransformerClass() {
return new String[0];
@@ -54,4 +60,14 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
public String getAccessTransformerClass() {
return null;
}
@Override
public String getMixinConfig() {
return "mixins.chunkapi.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return IMixins.getEarlyMixins(Mixin.class, loadedCoreMods);
}
}
@@ -20,7 +20,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.mixins.common.vanilla;
package com.falsepattern.chunk.internal.mixin.mixins.common.base;
import com.falsepattern.chunk.internal.DataRegistryImpl;
import lombok.val;
@@ -20,7 +20,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.mixins.common.vanilla;
package com.falsepattern.chunk.internal.mixin.mixins.common.base;
import com.falsepattern.chunk.internal.impl.CustomPacketMultiBlockChange;
import lombok.val;
@@ -20,7 +20,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.mixins.common.vanilla;
package com.falsepattern.chunk.internal.mixin.mixins.common.base;
import com.falsepattern.chunk.internal.DataRegistryImpl;
import com.falsepattern.chunk.internal.mixin.helpers.LockHelper;
@@ -20,7 +20,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.mixins.common.vanilla;
package com.falsepattern.chunk.internal.mixin.mixins.common.base;
import com.falsepattern.chunk.internal.DataRegistryImpl;
import com.falsepattern.chunk.internal.impl.CustomPacketBlockChange;
@@ -20,7 +20,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.mixins.common.vanilla;
package com.falsepattern.chunk.internal.mixin.mixins.common.base;
import com.falsepattern.chunk.internal.BlockPosUtil;
import com.falsepattern.chunk.internal.DataRegistryImpl;
@@ -0,0 +1,155 @@
/*
* ChunkAPI
*
* Copyright (C) 2023-2025 FalsePattern, The MEGA Team, LegacyModdingMC contributors
* Copyright (C) 2025 Cardinalstar16
* 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 <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.mixins.common.thermos;
import com.falsepattern.chunk.internal.mixin.helpers.LockHelper;
import lombok.val;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.server.S21PacketChunkData;
import net.minecraft.network.play.server.S26PacketMapChunkBulk;
import java.io.IOException;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@Mixin(S26PacketMapChunkBulk.class)
public abstract class S26PacketMapChunkBulkMixin {
@Shadow(aliases = "field_149268_i",
remap = false)
private byte[] inflaterBuffer;
@Shadow(aliases = "field_149263_e",
remap = false)
private byte[] deflatedData;
@Shadow(aliases = "field_149266_a",
remap = false)
private int[] xPositions;
@Shadow(aliases = "field_149264_b",
remap = false)
private int[] zPositions;
@Shadow(aliases = "field_149265_c",
remap = false)
private int[] subChunkMasks;
@Shadow(aliases = "field_149262_d",
remap = false)
private int[] subChunkMSBMasks;
@Shadow(aliases = "field_149261_g",
remap = false)
private int deflatedSize;
@Shadow(aliases = "field_149267_h",
remap = false)
private boolean skylight;
@Shadow(aliases = "field_149260_f",
remap = false)
private byte[][] datas;
@Shadow(remap = false)
protected abstract void compress();
/**
* @author FalsePattern
* @reason Replace functionality
*/
@Overwrite
public void readPacketData(PacketBuffer data) throws IOException {
short chunkCount = data.readShort();
val sizes = new int[chunkCount];
for (int i = 0; i < chunkCount; i++) {
sizes[i] = data.readInt();
}
deflatedSize = data.readInt();
skylight = data.readBoolean();
xPositions = new int[chunkCount];
zPositions = new int[chunkCount];
subChunkMasks = new int[chunkCount];
subChunkMSBMasks = new int[chunkCount];
datas = new byte[chunkCount][];
while (!LockHelper.bufferLockS26PacketMapChunkBulk.tryLock()) {
Thread.yield();
}
byte[] buf;
try {
if (inflaterBuffer.length < deflatedSize) {
inflaterBuffer = new byte[deflatedSize];
}
data.readBytes(inflaterBuffer, 0, deflatedSize);
buf = new byte[S21PacketChunkData.func_149275_c() * chunkCount];
Inflater inflater = new Inflater();
inflater.setInput(inflaterBuffer, 0, deflatedSize);
try {
inflater.inflate(buf);
} catch (DataFormatException dataformatexception) {
throw new IOException("Bad compressed data format");
} finally {
inflater.end();
}
} finally {
LockHelper.bufferLockS26PacketMapChunkBulk.unlock();
}
int pos = 0;
for (int i = 0; i < chunkCount; ++i) {
val size = sizes[i];
xPositions[i] = data.readInt();
zPositions[i] = data.readInt();
subChunkMasks[i] = data.readUnsignedShort();
datas[i] = new byte[size];
System.arraycopy(buf, pos, datas[i], 0, size);
pos += size;
}
}
/**
* @author FalsePattern
* @author Cardinalstar16
* @reason Replace functionality for thermos
*/
@Overwrite
public void writePacketData(PacketBuffer data) {
compress();
data.writeShort(xPositions.length);
for (byte[] bytes : datas) {
data.writeInt(bytes.length);
}
data.writeInt(deflatedSize);
data.writeBoolean(skylight);
data.writeBytes(deflatedData, 0, deflatedSize);
for (int i = 0; i < xPositions.length; ++i) {
data.writeInt(xPositions[i]);
data.writeInt(zPositions[i]);
data.writeShort((short) (subChunkMasks[i] & 65535));
}
}
}
@@ -129,7 +129,7 @@ public abstract class S26PacketMapChunkBulkMixin {
pos += size;
}
}
/**
* @author FalsePattern
* @reason Replace functionality
@@ -22,23 +22,23 @@
package com.falsepattern.chunk.internal.mixin.plugin;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.ITargetedMod;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
import com.gtnewhorizon.gtnhmixins.LateMixin;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import org.jetbrains.annotations.NotNull;
import java.util.function.Predicate;
import java.util.List;
import java.util.Set;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.ITargetedMod.PredicateHelpers.contains;
@LateMixin
public class LateMixins implements ILateMixinLoader {
@Override
public String getMixinConfig() {
return "mixins.chunkapi.late.json";
}
@RequiredArgsConstructor
public enum TargetedMod implements ITargetedMod {
LOOKINGGLASS("Looking Glass", false, contains("lookingglass-"))
;
@Getter
private final String modName;
@Getter
private final boolean loadInDevelopment;
@Getter
private final Predicate<String> condition;
@Override
public @NotNull List<String> getMixins(Set<String> loadedMods) {
return IMixins.getLateMixins(Mixin.class, loadedMods);
}
}
@@ -22,41 +22,111 @@
package com.falsepattern.chunk.internal.mixin.plugin;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.IMixin;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.ITargetedMod;
import com.falsepattern.chunk.internal.Tags;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.MixinHelper;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.SidedMixins;
import com.falsepattern.chunk.internal.mixin.plugin.fplib.TaggedMod;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.intellij.lang.annotations.Language;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.BooleanSupplier;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.IMixin.PredicateHelpers.always;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.IMixin.PredicateHelpers.require;
import static com.falsepattern.chunk.internal.mixin.plugin.TargetMod.LookingGlass;
import static com.falsepattern.chunk.internal.mixin.plugin.TargetMod.Spool;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.MixinHelper.avoid;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.MixinHelper.builder;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.MixinHelper.mods;
import static com.falsepattern.chunk.internal.mixin.plugin.fplib.MixinHelper.require;
@RequiredArgsConstructor
public enum Mixin implements IMixin {
public enum Mixin implements IMixins {
// @formatter:off
AnvilChunkLoaderMixin(Side.COMMON, always(), "vanilla.AnvilChunkLoaderMixin"),
PlayerInstanceMixin(Side.COMMON, always(), "vanilla.PlayerInstanceMixin"),
S21PacketChunkDataMixin(Side.COMMON, always(), "vanilla.S21PacketChunkDataMixin"),
S22PacketMultiBlockChangeMixin(Side.COMMON, always(), "vanilla.S22PacketMultiBlockChangeMixin"),
S23PacketBlockChangeMixin(Side.COMMON, always(), "vanilla.S23PacketBlockChangeMixin"),
S26PacketMapChunkBulkMixin(Side.COMMON, always(), "vanilla.S26PacketMapChunkBulkMixin"),
VanillaCore(Phase.EARLY,
() -> !hasThermos(),
common("vanilla.S26PacketMapChunkBulkMixin")),
ThermosCore(Phase.EARLY,
() -> hasThermos(),
common("thermos.S26PacketMapChunkBulkMixin")),
CommonCore(Phase.EARLY,
common("base.PlayerInstanceMixin",
"base.S21PacketChunkDataMixin",
"base.S22PacketMultiBlockChangeMixin",
"base.S23PacketBlockChangeMixin"),
client("vanilla.NetHandlerPlayClientMixin")),
//from: https://github.com/BallOfEnergy1/ChunkAPI/commit/4f5c0e60e04b6892d206f5f5d93cb20ba6b45608
Core_NoSpool(Phase.EARLY,
avoid(Spool),
common("base.AnvilChunkLoaderMixin"),
client("vanilla.ChunkMixin")),
ChunkMixin(Side.CLIENT, always(), "vanilla.ChunkMixin"),
NetHandlerPlayClientMixin(Side.CLIENT, always(), "vanilla.NetHandlerPlayClientMixin"),
//region Looking Glass
PacketChunkInfoMixin(Side.COMMON, require(TargetedMod.LOOKINGGLASS), "lookingglass.PacketChunkInfoMixin"),
//endregion
;
Compat_LookingGlass(Phase.LATE,
require(LookingGlass),
common("lookingglass.PacketChunkInfoMixin")),
// @formatter:on
//region boilerplate
;
@Getter
private final Side side;
@Getter
private final Predicate<List<ITargetedMod>> filter;
@Getter
private final String mixin;
}
private final MixinBuilder builder;
Mixin(Phase phase, SidedMixins... mixins) {
this(builder(mixins).setPhase(phase));
}
Mixin(Phase phase, BooleanSupplier cond, SidedMixins... mixins) {
this(builder(cond, mixins).setPhase(phase));
}
Mixin(Phase phase, TaggedMod mod, SidedMixins... mixins) {
this(builder(mod, mixins).setPhase(phase));
}
Mixin(Phase phase, TaggedMod[] mods, SidedMixins... mixins) {
this(builder(mods, mixins).setPhase(phase));
}
Mixin(Phase phase, BooleanSupplier cond, TaggedMod mod, SidedMixins... mixins) {
this(builder(cond, mod, mixins).setPhase(phase));
}
Mixin(Phase phase, BooleanSupplier cond, TaggedMod[] mods, SidedMixins... mixins) {
this(builder(cond, mods, mixins).setPhase(phase));
}
private static SidedMixins common(@Language(value = "JAVA",
prefix = "import " + Tags.ROOT_PKG + ".internal.mixin.mixins.common.",
suffix = ";") String... mixins) {
return MixinHelper.common(mixins);
}
private static SidedMixins client(@Language(value = "JAVA",
prefix = "import " + Tags.ROOT_PKG + ".internal.mixin.mixins.client.",
suffix = ";") String... mixins) {
return MixinHelper.client(mixins);
}
private static SidedMixins server(@Language(value = "JAVA",
prefix = "import " + Tags.ROOT_PKG + ".mixin.mixins.server.",
suffix = ";") String... mixins) {
return MixinHelper.server(mixins);
}
//endregion
static boolean hasThermos()
{
try
{
Class.forName("thermos.ThermosClassTransformer");
return true;
}
catch(ClassNotFoundException ignored) {
return false;
}
}
}
@@ -0,0 +1,56 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin;
import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
import com.gtnewhorizon.gtnhmixins.builders.TargetModBuilder;
import lombok.Getter;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public enum TargetMod implements ITargetMod {
LookingGlass("com.xcompwiz.lookingglass.LookingGlass"),
Spool("com.gamma.spool.core.Spool"),
;
@Getter
private final TargetModBuilder builder;
TargetMod(@Language(value = "JAVA",
prefix = "import ",
suffix = ";") @NotNull String className) {
this(className, null);
}
TargetMod(@Language(value = "JAVA",
prefix = "import ",
suffix = ";") @NotNull String className, @Nullable Consumer<TargetModBuilder> cfg) {
builder = new TargetModBuilder();
builder.setTargetClass(className);
if (cfg != null) {
cfg.accept(builder);
}
}
}
@@ -1,77 +0,0 @@
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib 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.
*
* FalsePatternLib 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 FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import lombok.val;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
public interface IMixin {
String getMixin();
Side getSide();
Predicate<List<ITargetedMod>> getFilter();
default boolean shouldLoad(List<ITargetedMod> loadedMods) {
val side = getSide();
return (side == Side.COMMON
|| side == Side.SERVER && FMLLaunchHandler.side().isServer()
|| side == Side.CLIENT && FMLLaunchHandler.side().isClient()) && getFilter().test(loadedMods);
}
/**
* @since 0.10.0
*/
enum Side {
COMMON,
CLIENT,
SERVER
}
/**
* @since 0.10.0
*/
final class PredicateHelpers {
public static Predicate<List<ITargetedMod>> never() {
return (list) -> false;
}
public static Predicate<List<ITargetedMod>> condition(Supplier<Boolean> condition) {
return (list) -> condition.get();
}
public static Predicate<List<ITargetedMod>> always() {
return (list) -> true;
}
public static Predicate<List<ITargetedMod>> require(ITargetedMod mod) {
return (list) -> list.contains(mod);
}
public static Predicate<List<ITargetedMod>> avoid(ITargetedMod mod) {
return (list) -> !list.contains(mod);
}
}
}
@@ -1,218 +0,0 @@
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib 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.
*
* FalsePatternLib 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 FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import lombok.val;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.ApiStatus;
import org.spongepowered.asm.lib.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import net.minecraft.launchwrapper.Launch;
import java.io.File;
import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.nio.file.Files.walk;
public interface IMixinPlugin extends IMixinConfigPlugin {
Path MODS_DIRECTORY_PATH = FileUtil.getMinecraftHome().toPath().resolve("mods");
static Logger createLogger(String modName) {
return LogManager.getLogger(modName + " Mixin Loader");
}
/**
* @deprecated since 1.4.0
*/
@Deprecated
static File findJarOf(final ITargetedMod mod) {
File result = null;
try (val stream = walk(MODS_DIRECTORY_PATH)) {
result = stream.filter(mod::isMatchingJar).map(Path::toFile).findFirst().orElse(null);
} catch (Exception e) {
e.printStackTrace();
}
if (result == null) {
File found = null;
for (URL url : Launch.classLoader.getURLs()) {
try {
val file = new File(url.toURI());
Path path = file.toPath();
if (mod.isMatchingJar(path)) {
found = file;
break;
}
} catch (Exception ignored) {
}
}
result = found;
}
return result;
}
/**
* @since 1.4.0
*/
static Set<File> findJarsOf(IMixinPlugin self, final ITargetedMod mod) {
if (!self.useNewFindJar()) {
val jar = findJarOf(mod);
return jar == null ? Collections.emptySet() : Collections.singleton(jar);
}
val results = new HashSet<File>();
try (val stream = walk(MODS_DIRECTORY_PATH)) {
results.addAll(stream.filter(mod::isMatchingJar).map(Path::toFile).collect(Collectors.toSet()));
} catch (Exception e) {
e.printStackTrace();
}
for (URL url : Launch.classLoader.getURLs()) {
try {
val file = new File(url.toURI());
Path path = file.toPath();
if (mod.isMatchingJar(path)) {
results.add(file);
break;
}
} catch (Exception ignored) {
}
}
return results;
}
Logger getLogger();
IMixin[] getMixinEnumValues();
ITargetedMod[] getTargetedModEnumValues();
/**
* @since 1.4.0
*/
default boolean useNewFindJar() {
return false;
}
@Override
@ApiStatus.Internal
default void onLoad(String mixinPackage) {
}
@Override
@ApiStatus.Internal
default String getRefMapperConfig() {
return null;
}
@Override
@ApiStatus.Internal
default boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
return true;
}
@Override
@ApiStatus.Internal
default void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
}
@Override
@ApiStatus.Internal
default List<String> getMixins() {
val isDevelopmentEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
val targetedMods = getTargetedModEnumValues();
val loadedMods = Arrays.stream(targetedMods)
//Java 21 compat
.filter(new Predicate<ITargetedMod>() {
@Override
public boolean test(ITargetedMod mod) {
boolean loadJar = IMixinPlugin.this.loadJarOf(mod);
return (mod.isLoadInDevelopment() && isDevelopmentEnvironment)
|| loadJar;
}
})
.collect(Collectors.toList());
for (val mod : targetedMods) {
if (loadedMods.contains(mod)) {
getLogger().info("Found " + mod.getModName() + "! Integrating now...");
} else {
getLogger().info("Could not find " + mod.getModName() + "! Skipping integration....");
}
}
List<String> mixins = new ArrayList<>();
for (val mixin : getMixinEnumValues()) {
if (mixin.shouldLoad(loadedMods)) {
String mixinClass = mixin.getSide().name().toLowerCase() + "." + mixin.getMixin();
mixins.add(mixinClass);
getLogger().info("Loading mixin: " + mixinClass);
}
}
return mixins;
}
@ApiStatus.Internal
default boolean loadJarOf(final ITargetedMod mod) {
try {
val jars = findJarsOf(this, mod);
if (jars.isEmpty()) {
getLogger().info("Jar not found for {}", mod);
return false;
}
for (val jar: jars) {
getLogger().info("Attempting to add {} to the URL Class Path", jar);
try {
MinecraftURLClassPath.addJar(jar);
} catch (Throwable e) {
e.printStackTrace();
}
}
} catch (Throwable e) {
e.printStackTrace();
}
return true;
}
@Override
@ApiStatus.Internal
default void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
@Override
@ApiStatus.Internal
default void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
}
@@ -1,59 +0,0 @@
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib 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.
*
* FalsePatternLib 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 FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import com.google.common.io.Files;
import java.nio.file.Path;
import java.util.function.Predicate;
public interface ITargetedMod {
String getModName();
boolean isLoadInDevelopment();
@SuppressWarnings("UnstableApiUsage")
default boolean isMatchingJar(Path path) {
String pathString = path.toString();
String nameLowerCase = Files.getNameWithoutExtension(pathString).toLowerCase();
String fileExtension = Files.getFileExtension(pathString);
return "jar".equals(fileExtension) && getCondition().test(nameLowerCase);
}
Predicate<String> getCondition();
final class PredicateHelpers {
public static Predicate<String> startsWith(String subString) {
return (name) -> name.startsWith(subString.toLowerCase());
}
public static Predicate<String> contains(String subString) {
return (name) -> name.contains(subString.toLowerCase());
}
public static Predicate<String> matches(String regex) {
return (name) -> name.matches(regex);
}
}
}
@@ -1,19 +0,0 @@
FalsePatternLib
Copyright (C) 2022-2025 FalsePattern
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 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 <https://www.gnu.org/licenses/>.
@@ -0,0 +1,112 @@
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib 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.
*
* FalsePatternLib 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 FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer;
import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
import lombok.val;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.BooleanSupplier;
public class MixinHelper {
public static @NotNull MixinBuilder builder(@NotNull SidedMixins @NotNull ... mixins) {
return builderImpl(null, null, mixins);
}
public static @NotNull MixinBuilder builder(@Nullable BooleanSupplier cond, @NotNull SidedMixins @NotNull ... mixins) {
return builderImpl(cond, null, mixins);
}
public static @NotNull MixinBuilder builder(@NotNull TaggedMod mod, @NotNull SidedMixins @NotNull ... mixins) {
return builderImpl(null, mods(mod), mixins);
}
public static @NotNull MixinBuilder builder(@NotNull TaggedMod @Nullable [] mods, @NotNull SidedMixins @NotNull ... mixins) {
return builderImpl(null, mods, mixins);
}
public static @NotNull MixinBuilder builder(@NotNull BooleanSupplier cond, @NotNull TaggedMod mod, @NotNull SidedMixins @NotNull ... mixins) {
return builderImpl(cond, mods(mod), mixins);
}
public static @NotNull MixinBuilder builder(@NotNull BooleanSupplier cond, @NotNull TaggedMod @NotNull [] mods, @NotNull SidedMixins @NotNull ... mixins) {
return builderImpl(cond, mods, mixins);
}
private static @NotNull MixinBuilder builderImpl(@Nullable BooleanSupplier cond, @NotNull TaggedMod @Nullable [] requiredMods, @NotNull SidedMixins @NotNull ... mixins) {
val builder = new MixinBuilder();
if (requiredMods != null) {
for (val mod : requiredMods) {
if (mod.require) {
builder.addRequiredMod(mod.mod);
} else {
builder.addExcludedMod(mod.mod);
}
}
}
for (val mixin : mixins) {
builder.addSidedMixins(mixin.side, mixin.mixins);
}
if (cond != null) {
builder.setApplyIf(cond::getAsBoolean);
}
return builder;
}
public static @NotNull SidedMixins common(@NotNull String @NotNull ... mixins) {
for (int i = 0; i < mixins.length; i++) {
mixins[i] = "common." + mixins[i];
}
return new SidedMixins(IBaseTransformer.Side.COMMON, mixins);
}
public static @NotNull SidedMixins client(@NotNull String @NotNull ... mixins) {
for (int i = 0; i < mixins.length; i++) {
mixins[i] = "client." + mixins[i];
}
return new SidedMixins(IBaseTransformer.Side.CLIENT, mixins);
}
public static @NotNull SidedMixins server(@NotNull String @NotNull ... mixins) {
for (int i = 0; i < mixins.length; i++) {
mixins[i] = "server." + mixins[i];
}
return new SidedMixins(IBaseTransformer.Side.SERVER, mixins);
}
public static @NotNull TaggedMod require(@NotNull ITargetMod mod) {
return new TaggedMod(true, mod);
}
public static @NotNull TaggedMod avoid(@NotNull ITargetMod mod) {
return new TaggedMod(false, mod);
}
public static @NotNull TaggedMod @NotNull [] mods(@NotNull TaggedMod @NotNull ... t) {
return t;
}
}
@@ -1 +0,0 @@
Shadowed from FalsePatternLib 1.7.0
@@ -19,23 +19,15 @@
* You should have received a copy of the GNU Lesser General Public License
* along with FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import com.gtnewhorizon.gtnhmixins.builders.IBaseTransformer;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* Backport from spongemixins 1.3 for compat with the curseforge 1.2.0 version.
* <p>
* Also added Grimoire protection.
*
* @since 0.10.0
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class MinecraftURLClassPath {
public static void addJar(File pathToJar) throws Exception {
UCPImpl.addJar(pathToJar);
}
@RequiredArgsConstructor
public class SidedMixins {
public final IBaseTransformer.@NotNull Side side;
public final @NotNull String @NotNull [] mixins;
}
@@ -19,29 +19,15 @@
* You should have received a copy of the GNU Lesser General Public License
* along with FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import lombok.experimental.UtilityClass;
import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import net.minecraft.launchwrapper.Launch;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @since 0.8.2
*/
@UtilityClass
public final class FileUtil {
public static File getMinecraftHome() {
return getMinecraftHomePath().toFile();
}
/**
* @since 1.0.0
*/
public static Path getMinecraftHomePath() {
return Launch.minecraftHome == null ? Paths.get("").toAbsolutePath() : Launch.minecraftHome.toPath();
}
}
@RequiredArgsConstructor
public class TaggedMod {
public final boolean require;
public final @NotNull ITargetMod mod;
}
@@ -0,0 +1,57 @@
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib 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.
*
* FalsePatternLib 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 FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
import com.gtnewhorizon.gtnhmixins.builders.TargetModBuilder;
import lombok.Getter;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public class TargetMod implements ITargetMod {
@Getter
private final TargetModBuilder builder;
private final String name;
public TargetMod(@NotNull String name, @Language(value = "JAVA", prefix = "import ", suffix = ";") @NotNull String className) {
this(name, className, null);
}
public TargetMod(@NotNull String name, @Language(value = "JAVA", prefix = "import ", suffix = ";") @NotNull String className, @Nullable Consumer<TargetModBuilder> cfg) {
this.name = name;
builder = new TargetModBuilder();
builder.setTargetClass(className);
if (cfg != null) {
cfg.accept(builder);
}
}
@Override
public String toString() {
return name;
}
}
@@ -1,61 +0,0 @@
/*
* This file is part of FalsePatternLib.
*
* Copyright (C) 2022-2025 FalsePattern
* All Rights Reserved
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* FalsePatternLib 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.
*
* FalsePatternLib 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 FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.chunk.internal.mixin.plugin.fplib;
import lombok.val;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.io.IOException;
public final class UCPImpl {
private static final boolean GRIMOIRE;
static {
boolean grimoire = false;
String[] knownGrimoireClassNames =
new String[]{"io.github.crucible.grimoire.Grimoire", "io.github.crucible.grimoire.common.GrimoireCore"};
for (val className : knownGrimoireClassNames) {
try {
if (Launch.classLoader.getClassBytes(className) != null) {
grimoire = true;
break;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
GRIMOIRE = grimoire;
}
public static void addJar(File pathToJar) throws Exception {
if (!GRIMOIRE) {
final LaunchClassLoader loader = Launch.classLoader;
loader.addURL(pathToJar.toURI().toURL());
// Act as-if we only added the mod to ucp
loader.getSources().remove(loader.getSources().size() - 1);
}
}
}
@@ -1,23 +1,23 @@
/*
* ChunkAPI
* This file is part of FalsePatternLib.
*
* Copyright (C) 2023-2025 FalsePattern, The MEGA Team, LegacyModdingMC contributors
* Copyright (C) 2022-2025 FalsePattern
* 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
* FalsePatternLib 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,
* FalsePatternLib 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
* 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 <https://www.gnu.org/licenses/>.
* along with FalsePatternLib. If not, see <https://www.gnu.org/licenses/>.
*/
@ApiStatus.Internal
@@ -40,7 +40,7 @@ import java.nio.ByteBuffer;
import static com.falsepattern.chunk.internal.Common.BLOCKS_PER_SUBCHUNK;
import static com.falsepattern.chunk.internal.Common.SUBCHUNKS_PER_CHUNK;
public class BlockIDManager extends VanillaManager implements DataManager.PacketDataManager, DataManager.BlockPacketDataManager, DataManager.SubChunkDataManager {
public class BlockIDManager extends VanillaManager implements DataManager.PacketDataManager, DataManager.CubicPacketDataManager, DataManager.BlockPacketDataManager, DataManager.SubChunkDataManager {
private static final int LSB_BYTES_PER_SUBCHUNK = BLOCKS_PER_SUBCHUNK;
private static final int MSB_BYTES_PER_SUBCHUNK = BLOCKS_PER_SUBCHUNK / 2;
private static final int HEADER_SIZE = 2;
@@ -100,6 +100,36 @@ public class BlockIDManager extends VanillaManager implements DataManager.Packet
}
}
@Override
public int maxPacketSizeCubic() {
return HEADER_SIZE + LSB_BYTES_PER_SUBCHUNK + MSB_BYTES_PER_SUBCHUNK;
}
@Override
public void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.put(blockStorage.getBlockLSBArray());
if (blockStorage.getBlockMSBArray() != null) {
buffer.put((byte) 1);
buffer.put(blockStorage.getBlockMSBArray().data);
} else {
buffer.put((byte) 0);
}
}
@Override
public void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.get(blockStorage.getBlockLSBArray());
if (buffer.get() != 0) {
if (blockStorage.getBlockMSBArray() == null) {
blockStorage.createBlockMSBArray();
}
buffer.get(blockStorage.getBlockMSBArray().data);
}
}
@Override
public boolean subChunkPrivilegedAccess() {
return true;
@@ -32,7 +32,7 @@ import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.nio.ByteBuffer;
public abstract class NibbleManager extends VanillaManager implements DataManager.PacketDataManager {
public abstract class NibbleManager extends VanillaManager implements DataManager.PacketDataManager, DataManager.CubicPacketDataManager {
public static final int BYTES_PER_SUBCHUNK = Common.BLOCKS_PER_SUBCHUNK / 2;
protected abstract NibbleArray getNibbleArray(ExtendedBlockStorage subChunk);
@@ -67,4 +67,19 @@ public abstract class NibbleManager extends VanillaManager implements DataManage
}
}
}
@Override
public int maxPacketSizeCubic() {
return BYTES_PER_SUBCHUNK;
}
@Override
public void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.put(getNibbleArray(blockStorage).data, 0, BYTES_PER_SUBCHUNK);
}
@Override
public void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
buffer.get(getNibbleArray(blockStorage).data, 0, BYTES_PER_SUBCHUNK);
}
}
@@ -57,6 +57,20 @@ public class SkylightManager extends NibbleManager implements DataManager.SubChu
}
}
@Override
public void writeToBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
if (!chunk.worldObj.provider.hasNoSky) {
super.writeToBuffer(chunk, blockStorage, buffer);
}
}
@Override
public void readFromBuffer(Chunk chunk, ExtendedBlockStorage blockStorage, ByteBuffer buffer) {
if (!chunk.worldObj.provider.hasNoSky) {
super.readFromBuffer(chunk, blockStorage, buffer);
}
}
@Override
public boolean subChunkPrivilegedAccess() {
return true;
@@ -2,7 +2,6 @@
"required": true,
"minVersion": "0.8.5",
"package": "com.falsepattern.chunk.internal.mixin.mixins",
"plugin": "com.falsepattern.chunk.internal.mixin.plugin.MixinPlugin",
"refmap": "mixins.chunkapi.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
@@ -0,0 +1,9 @@
{
"required": true,
"minVersion": "0.8.5",
"package": "com.falsepattern.chunk.internal.mixin.mixins",
"refmap": "mixins.chunkapi.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}