Modded server registries

- Modded platform hands the running server to registry access
- Tile reader is built from the running server
- Registry test covers startup, reload and shutdown of dynamic registries
This commit is contained in:
Brian Neumann-Fopiano
2026-09-29 00:29:25 -04:00
parent 241260a762
commit c20296d106
4 changed files with 45 additions and 21 deletions
@@ -391,10 +391,7 @@ public final class ModdedEngineBootstrap {
BlockDataMergeSupport.StateMerger previousMerger = BlockDataMergeSupport.bindPlatformMerger(new NativeStateMerger(ModdedBlockResolution.BLOCKS)::merge);
rollback.add(() -> BlockDataMergeSupport.restorePlatformMerger(previousMerger));
NativeTileReader nativeTileReader = new NativeTileReader(() -> {
NativeModdedServer server = boundLoader.currentServer();
return server == null ? null : server.registryAccess();
});
NativeTileReader nativeTileReader = NativeTileReader.forServer(boundLoader::currentServer);
TileData.TileReader previousTileReader = TileData.bindPlatformReader(in -> ModdedTileData.wrap(nativeTileReader.read(in)));
rollback.add(() -> TileData.restorePlatformReader(previousTileReader));
@@ -38,8 +38,6 @@ import art.arcane.iris.spi.PlatformScheduler;
import art.arcane.iris.spi.PlatformStructureHooks;
import art.arcane.volmlib.nativelib.terrain.NativeWorld;
import net.minecraft.core.HolderLookup;
import java.io.File;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -64,7 +62,7 @@ public final class ModdedPlatform implements IrisPlatform {
this.loader = loader;
this.registries = new ModdedRegistries(
new NativeRegistryAccess(new NativeRegistryAccess.Configuration(
ModdedPlatform::nativeRegistries, ModdedPlatform::reloadableRegistries,
ModdedEngineBootstrap::currentServer,
ModdedRegistries::warnNotReady)),
ModdedPlatform::generationRegistry);
this.scheduler = new ModdedScheduler();
@@ -72,16 +70,6 @@ public final class ModdedPlatform implements IrisPlatform {
this.biomeWriter = new ModdedBiomeWriter(new NativeBiomeRegistry(ModdedEngineBootstrap::currentServer, "minecraft:plains"));
}
private static HolderLookup.Provider nativeRegistries() {
NativeModdedServer server = ModdedEngineBootstrap.currentServer();
return server == null ? null : server.registryAccess();
}
private static HolderLookup.Provider reloadableRegistries() {
NativeModdedServer server = ModdedEngineBootstrap.currentServer();
return server == null ? null : server.reloadableRegistries();
}
public static void errorSink(Consumer<Throwable> sink) {
ERROR_SINK = sink;
}
@@ -41,7 +41,7 @@ public class ModdedTileParityTest {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
registries = RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY);
NativeTileReader nativeTileReader = new NativeTileReader(() -> null);
NativeTileReader nativeTileReader = NativeTileReader.forServer(() -> null);
TileData.bindPlatformReader(in -> ModdedTileData.wrap(nativeTileReader.read(in)));
TileData.bindPlatformFactory(ModdedTileData::fromProperties);
}
@@ -1,20 +1,31 @@
package art.arcane.iris.modded;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeRegistryAccess;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer;
import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty;
import net.minecraft.SharedConstants;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.MinecraftServer;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NativeRegistryAccessTest {
@BeforeClass
@@ -25,7 +36,7 @@ public class NativeRegistryAccessTest {
@Test
public void statePropertiesPreserveTypesDefaultsAndSharedGroups() {
NativeRegistryAccess access = new NativeRegistryAccess(new NativeRegistryAccess.Configuration(() -> null, () -> null, message -> {}));
NativeRegistryAccess access = new NativeRegistryAccess(new NativeRegistryAccess.Configuration(() -> null, message -> {}));
Map<String, List<NativeBlockProperty>> properties = access.blockStateProperties();
assertSame(properties.get("minecraft:stone"), properties.get("minecraft:dirt"));
@@ -41,7 +52,7 @@ public class NativeRegistryAccessTest {
@Test
public void staticRegistriesResolveWithoutAnActiveServerAndDynamicReadsStayEmpty() {
List<String> warnings = new ArrayList<>();
NativeRegistryAccess access = new NativeRegistryAccess(new NativeRegistryAccess.Configuration(() -> null, () -> null, warnings::add));
NativeRegistryAccess access = new NativeRegistryAccess(new NativeRegistryAccess.Configuration(() -> null, warnings::add));
assertEquals("minecraft:diamond_sword", access.item("DIAMOND SWORD").key());
assertEquals("creature", access.entity("minecraft:cow").spawnCategory());
@@ -49,6 +60,34 @@ public class NativeRegistryAccessTest {
assertTrue(access.biomeKeys().isEmpty());
assertTrue(access.structureKeys().isEmpty());
assertTrue(access.enchantmentKeys().isEmpty());
assertEquals(List.of("biome", "structure", "enchantment"), warnings);
assertTrue(access.lootTableKeys().isEmpty());
assertEquals(List.of("biome", "structure", "enchantment", "loot table"), warnings);
}
@Test
public void dynamicRegistriesFollowServerStartupReloadAndShutdown() {
AtomicReference<NativeModdedServer> activeServer = new AtomicReference<>();
List<String> warnings = new ArrayList<>();
NativeRegistryAccess access = new NativeRegistryAccess(new NativeRegistryAccess.Configuration(activeServer::get, warnings::add));
assertTrue(access.lootTableKeys().isEmpty());
MinecraftServer server = mock(MinecraftServer.class, RETURNS_DEEP_STUBS);
when(server.registryAccess().lookupOrThrow(Registries.BIOME).listElementIds())
.thenAnswer(invocation -> Stream.of(ResourceKey.create(Registries.BIOME, Identifier.parse("example:biome"))));
when(server.reloadableRegistries().lookup().lookupOrThrow(Registries.LOOT_TABLE).listElementIds())
.thenAnswer(invocation -> Stream.of(ResourceKey.create(Registries.LOOT_TABLE, Identifier.parse("example:initial"))));
activeServer.set(NativeModdedServer.fromHandle(server));
assertEquals(List.of("example:biome"), access.biomeKeys());
assertEquals(List.of("example:initial"), access.lootTableKeys());
HolderLookup.Provider reloadedRegistries = mock(HolderLookup.Provider.class, RETURNS_DEEP_STUBS);
when(reloadedRegistries.lookupOrThrow(Registries.LOOT_TABLE).listElementIds())
.thenAnswer(invocation -> Stream.of(ResourceKey.create(Registries.LOOT_TABLE, Identifier.parse("example:reloaded"))));
when(server.reloadableRegistries().lookup()).thenReturn(reloadedRegistries);
assertEquals(List.of("example:reloaded"), access.lootTableKeys());
activeServer.set(null);
assertTrue(access.biomeKeys().isEmpty());
assertTrue(access.lootTableKeys().isEmpty());
assertEquals(List.of("loot table", "biome", "loot table"), warnings);
}
}