Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
at master 166 lines 8.7 kB view raw
1package net.lerariemann.infinity.util.loading; 2 3import com.mojang.serialization.Codec; 4import net.lerariemann.infinity.access.RegistryAccess; 5import net.lerariemann.infinity.dimensions.RandomDimension; 6import net.lerariemann.infinity.registry.payload.ModPayloads; 7import net.lerariemann.infinity.util.VersionMethods; 8import net.minecraft.client.Minecraft; 9import net.minecraft.core.Registry; 10import net.minecraft.core.WritableRegistry; 11import net.minecraft.core.registries.Registries; 12import net.minecraft.nbt.CompoundTag; 13import net.minecraft.resources.RegistryOps; 14import net.minecraft.resources.ResourceKey; 15import net.minecraft.resources.ResourceLocation; 16import net.minecraft.server.MinecraftServer; 17import net.minecraft.world.level.biome.Biome; 18import net.minecraft.world.level.dimension.DimensionType; 19import net.minecraft.world.level.dimension.LevelStem; 20import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; 21import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; 22import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; 23import net.minecraft.world.level.levelgen.placement.PlacedFeature; 24import net.minecraft.world.level.levelgen.structure.Structure; 25import net.minecraft.world.level.levelgen.structure.StructureSet; 26import org.jspecify.annotations.NonNull; 27 28import java.nio.file.Path; 29import java.nio.file.Paths; 30import java.util.*; 31 32/** 33 * Prepares Minecraft's registries for runtime injection of generated content. 34 * Delegates the act of injection itself to per-registry {@link JsonGrabber}s. 35 * @see net.lerariemann.infinity.mixin.core.SimpleRegistryMixin 36 */ 37public class DimensionGrabber { 38 net.minecraft.core.RegistryAccess baseRegistryManager; 39 Set<WritableRegistry<?>> mutableRegistries = new HashSet<>(); 40 RegistryOps.RegistryInfoLookup registryInfoGetter; 41 42 public DimensionGrabber(net.minecraft.core.RegistryAccess brm) { 43 this(brm, Set.of(Registries.CONFIGURED_FEATURE, 44 Registries.PLACED_FEATURE, 45 Registries.CONFIGURED_CARVER, 46 Registries.BIOME, 47 Registries.STRUCTURE, 48 Registries.STRUCTURE_SET, 49 Registries.NOISE_SETTINGS, 50 Registries.DIMENSION_TYPE, 51 Registries.LEVEL_STEM)); 52 } 53 54 /** 55 * @param unfrozenKeys a set of keys for all registries we want to modify. 56 * unfortunately, unfreezing and refreezing literally everything leads to problems on neoforge servers 57 */ 58 public DimensionGrabber(net.minecraft.core.RegistryAccess brm, Set<ResourceKey<? extends Registry<?>>> unfrozenKeys) { 59 baseRegistryManager = brm; 60 baseRegistryManager.registries().forEach((entry) -> { 61 if (unfrozenKeys.contains(entry.key())) { 62 ((RegistryAccess)entry.value()).infinity$unfreeze(); 63 mutableRegistries.add((WritableRegistry<?>)entry.value()); 64 } 65 }); 66 registryInfoGetter = getGetter(); 67 } 68 69 /** 70 * Grabs everything within a dimension's datapack. 71 * @return a {@link LevelStem} object containing all necessary headers for creating the actual playable {@link net.minecraft.world.level.Level}. 72 */ 73 public static LevelStem readDimensionFromDisk(RandomDimension d) { 74 DimensionGrabber grabber = new DimensionGrabber(d.server.registryAccess()); 75 Path rootdir = Paths.get(d.getStoragePath()); 76 grabber.grabAllRelatedData(rootdir); 77 LevelStem options = grabber.grabDimension(rootdir, d.getName()); 78 grabber.close(); 79 return options; 80 } 81 82 /** 83 * Grabs contents of all files in a directory. Used by {@link net.lerariemann.infinity.util.config.SoundScanner} to runtime-inject jukebox song definition data. 84 */ 85 public static <T> void readCategoryFromDisk(MinecraftServer server, Codec<T> codec, ResourceKey<Registry<T>> registryKey, Path path) { 86 DimensionGrabber grabber = new DimensionGrabber(server.registryAccess(), Set.of(registryKey)); 87 grabber.buildGrabber(codec, registryKey).grabAll(path); 88 grabber.close(); 89 } 90 91 /** 92 * Inserts one object into one registry. Used on the client to insert individual dimension types and biomes when the server asks it to, since the client 93 * does not know or care about everything else a dimension datapack holds. 94 * @see ModPayloads 95 */ 96 public static <T> void grabObjectForClient(Minecraft client, Codec<T> codec, ResourceKey<Registry<T>> registryKey, 97 ResourceLocation id, CompoundTag data) { 98 if (data.isEmpty()) return; 99 DimensionGrabber grabber = new DimensionGrabber( 100 Objects.requireNonNull(client.getConnection()).registryAccess(), 101 Set.of(registryKey) 102 ); 103 grabber.grabObjectForClient(codec, registryKey, id, data); 104 grabber.close(); 105 } 106 107 private void grabAllRelatedData(Path rootdir) { 108 buildGrabber(ConfiguredFeature.DIRECT_CODEC, Registries.CONFIGURED_FEATURE).grabAll(rootdir.resolve("worldgen/configured_feature")); 109 buildGrabber(PlacedFeature.DIRECT_CODEC, Registries.PLACED_FEATURE).grabAll(rootdir.resolve("worldgen/placed_feature"), true); 110 buildGrabber(ConfiguredWorldCarver.DIRECT_CODEC, Registries.CONFIGURED_CARVER).grabAll(rootdir.resolve("worldgen/configured_carver")); 111 buildGrabber(Biome.DIRECT_CODEC, Registries.BIOME).grabAll(rootdir.resolve("worldgen/biome")); 112 buildGrabber(Structure.DIRECT_CODEC, Registries.STRUCTURE).grabAll(rootdir.resolve("worldgen/structure")); 113 buildGrabber(StructureSet.DIRECT_CODEC, Registries.STRUCTURE_SET).grabAll(rootdir.resolve("worldgen/structure_set")); 114 buildGrabber(NoiseGeneratorSettings.DIRECT_CODEC, Registries.NOISE_SETTINGS).grabAll(rootdir.resolve("worldgen/noise_settings")); 115 buildGrabber(DimensionType.DIRECT_CODEC, Registries.DIMENSION_TYPE).grabAll(rootdir.resolve("dimension_type")); 116 } 117 118 private LevelStem grabDimension(Path rootdir, String i) { 119 LevelStem ret = buildGrabber(LevelStem.CODEC, Registries.LEVEL_STEM).grabWithReturn(rootdir.toString()+"/dimension", i, false); 120 close(); 121 return ret; 122 } 123 124 private <T> JsonGrabber<T> buildGrabber(Codec<T> codec, ResourceKey<Registry<T>> key) { 125 return (new JsonGrabber<>(registryInfoGetter, codec, (WritableRegistry<T>) (VersionMethods.getRegistry(baseRegistryManager, key)))); 126 } 127 128 private <T> void grabObjectForClient(Codec<T> codec, ResourceKey<Registry<T>> key, ResourceLocation id, CompoundTag optiondata) { 129 if (!(VersionMethods.getRegistry(baseRegistryManager, key).containsKey(ResourceKey.create(key, id)))) buildGrabber(codec, key).grab(id, optiondata, false); 130 } 131 132 /** Freezes all the registries after the grabbing is done, since registries can't be left unfrozen when the game's running. */ 133 private void close() { 134 baseRegistryManager.registries().forEach((entry) -> entry.value().freeze()); 135 } 136 137 /** 138 * Creates and returns a {@link RegistryOps.RegistryInfoLookup}, which will be used by {@link JsonGrabber}s to translate raw json into all sorts of objects. 139 */ 140 private RegistryOps.RegistryInfoLookup getGetter() { 141 final Map<ResourceKey<? extends Registry<?>>, RegistryOps.RegistryInfo<?>> map = new HashMap<>(); 142 baseRegistryManager.registries().forEach((entry) -> map.put(entry.key(), createInfo(entry.value()))); 143 mutableRegistries.forEach(registry -> map.put(registry.key(), createMutableInfo(registry))); 144 return new RegistryOps.RegistryInfoLookup() { 145 public <T> @NonNull Optional<RegistryOps.RegistryInfo<T>> lookup(ResourceKey<? extends Registry<? extends T>> registryRef) { 146 return Optional.ofNullable((RegistryOps.RegistryInfo<T>)map.get(registryRef)); 147 } 148 }; 149 } 150 151 public static <T> RegistryOps.RegistryInfo<T> createMutableInfo(WritableRegistry<T> registry) { 152 //? if >1.21.2 { 153 return new RegistryOps.RegistryInfo<>(registry, registry.createRegistrationLookup(), registry.registryLifecycle()); 154 //?} else { 155 /*return new RegistryOps.RegistryInfo<>(registry.asLookup(), registry.createRegistrationLookup(), registry.registryLifecycle()); 156 *///?} 157 } 158 public static <T> RegistryOps.RegistryInfo<T> createInfo(Registry<T> registry) { 159 //? if >1.21.2 { 160 return new RegistryOps.RegistryInfo<>(registry, registry, registry.registryLifecycle()); 161 //?} else { 162 /*return new RegistryOps.RegistryInfo<>(registry.asLookup(), registry.asTagAddingLookup(), registry.registryLifecycle()); 163 *///?} 164 165 } 166}