Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
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}