Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
1package net.lerariemann.infinity.dimensions;
2
3import net.lerariemann.infinity.InfinityMod;
4import net.lerariemann.infinity.options.RandomInfinityOptions;
5import net.lerariemann.infinity.util.VersionMethods;
6import net.lerariemann.infinity.util.core.CommonIO;
7import net.lerariemann.infinity.util.InfinityMethods;
8import net.lerariemann.infinity.util.core.ConfigType;
9import net.lerariemann.infinity.util.core.NbtUtils;
10import net.lerariemann.infinity.util.core.RandomProvider;
11import net.lerariemann.infinity.util.platform.InfinityPlatform;
12import net.minecraft.core.Registry;
13import net.minecraft.core.registries.Registries;
14import net.minecraft.nbt.*;
15import net.minecraft.resources.ResourceKey;
16import net.minecraft.resources.ResourceLocation;
17import net.minecraft.server.MinecraftServer;
18import net.minecraft.tags.BiomeTags;
19import net.minecraft.tags.TagKey;
20import net.minecraft.util.Mth;
21import net.minecraft.world.level.biome.Biome;
22import net.minecraft.world.level.storage.LevelResource;
23import java.io.IOException;
24import java.nio.file.Files;
25import java.nio.file.Paths;
26import java.util.*;
27
28public class RandomDimension {
29 public final long numericId;
30 public final RandomProvider PROVIDER;
31 public ResourceLocation identifier;
32 public final Random random;
33 public int height;
34 public int min_y;
35 public int sea_level;
36 public boolean randomiseblocks;
37 public CompoundTag default_block;
38 public CompoundTag deepslate;
39 public CompoundTag default_fluid;
40 public List<CompoundTag> additional_blocks;
41 public List<String> vanilla_biomes;
42 public List<Long> random_biome_ids;
43 public List<RandomBiome> random_biomes;
44 public Map<String, CompoundTag> top_blocks;
45 public Map<String, List<String>> structure_ids;
46 public Map<String, CompoundTag> underwater;
47 public String type_alike;
48 public MinecraftServer server;
49 public CompoundTag data;
50 public RandomDimensionType type;
51
52 public RandomDimension(ResourceLocation id, MinecraftServer server) {
53 this.server = server;
54 PROVIDER = InfinityMod.provider;
55 identifier = id;
56 numericId = InfinityMethods.getNumericFromId(identifier);
57 random = new Random(numericId);
58 initializeStorage();
59 /* Code for easter dimensions */
60 if (PROVIDER.easterizer.easterize(this)) {
61 wrap_up(true);
62 return;
63 }
64 /* Code for procedurally generated dimensions */
65 genBasics();
66 type = new RandomDimensionType(this);
67 data.putString("type", type.fullname);
68 data.put("generator", randomDimensionGenerator());
69 for (Long l: random_biome_ids) if (doesNotContain(Registries.BIOME, "biome_"+l)) {
70 RandomBiome b = new RandomBiome(l, this);
71 random_biomes.add(b);
72 addStructures(b);
73 }
74 writeTags(getRootPath());
75 wrap_up(false);
76 }
77
78 public String getName() {
79 return identifier.getPath();
80 }
81
82 public String getRootPath() {
83 return server.getWorldPath(LevelResource.DATAPACK_DIR).resolve(getName()).toString();
84 }
85
86 public String getStoragePath() {
87 return server.getWorldPath(LevelResource.DATAPACK_DIR).resolve(getName()).resolve("data").resolve(InfinityMod.MOD_ID).toString();
88 }
89
90 public void initializeStorage() {
91 data = new CompoundTag();
92 vanilla_biomes = new ArrayList<>();
93 random_biome_ids = new ArrayList<>();
94 random_biomes = new ArrayList<>();
95 top_blocks = new HashMap<>();
96 underwater = new HashMap<>();
97 structure_ids = new HashMap<>();
98 additional_blocks = new ArrayList<>();
99 }
100
101 public void genBasics() {
102 type_alike = PROVIDER.randomName(random, ConfigType.NOISE_PRESETS);
103 min_y = 16*Mth.clamp((int)Math.floor(random.nextExponential() * 2), isOverworldLike() ? -125 : -3, 0);
104 int avgHeight = Mth.clamp(RandomProvider.ruleInt("avgDimensionHeight"), 64, 1024);
105 int max_y = 16*Mth.clamp((int)Math.floor(random.nextGaussian(avgHeight/16.0, avgHeight/64.0)), isOverworldLike() ? 1 : 5, 125);
106 randomiseblocks = PROVIDER.roll(random, "randomise_blocks");
107 int sea_level_default = 63;
108 if (!isOverworldLike()) sea_level_default = switch(type_alike) {
109 case "minecraft:floating_islands" -> -64;
110 case "minecraft:end" -> 0;
111 case "minecraft:nether", "minecraft:caves" -> 32;
112 default -> 63;
113 };
114 sea_level = randomiseblocks ? (int)Math.floor(random.nextGaussian(sea_level_default, 8)) : sea_level_default;
115 max_y = Math.max(max_y, 16 * (int) (1 + Math.floor(sea_level / 16.0)));
116 height = max_y - min_y;
117 default_block = randomiseblocks ?
118 PROVIDER.randomElement(random, ConfigType.FULL_BLOCKS_WG) :
119 NbtUtils.nameToElement(getDefaultBlock("minecraft:stone"));
120 default_fluid = randomiseblocks ?
121 PROVIDER.randomElement(random, ConfigType.FLUIDS) :
122 NbtUtils.nameToFluid(getDefaultFluid());
123 deepslate = Arrays.stream((new String[]{"minecraft:overworld", "minecraft:amplified", "infinity:whack"})).toList().contains(type_alike) ?
124 NbtUtils.nameToElement("minecraft:deepslate") : default_block;
125 }
126
127 void wrap_up(boolean isEasterDim) {
128 if (!isEasterDim) (new DimensionData(this)).save();
129 (new RandomInfinityOptions(this, isEasterDim)).save();
130 CommonIO.write(data, getStoragePath() + "/dimension", getName() + ".json");
131 if (!(Paths.get(getRootPath() + "/pack.mcmeta")).toFile().exists())
132 CommonIO.write(InfinityPlatform.dataPackMcmeta("Dimension #" + numericId), getRootPath(), "pack.mcmeta");
133 }
134
135 String getDefaultBlock(String fallback) {
136 switch(type_alike) {
137 case "minecraft:end" -> {
138 return "minecraft:end_stone";
139 }
140 case "minecraft:nether" -> {
141 return "minecraft:netherrack";
142 }
143 default -> {
144 return fallback;
145 }
146 }
147 }
148 String getDefaultFluid() {
149 switch(type_alike) {
150 case "minecraft:end" -> {
151 return "minecraft:air";
152 }
153 case "minecraft:nether" -> {
154 return "minecraft:lava";
155 }
156 default -> {
157 return "minecraft:water";
158 }
159 }
160 }
161
162 public <T> boolean doesNotContain(ResourceKey<? extends Registry<T>> key, String name) {
163 //? if >1.21.4 {
164 return !(server.registryAccess().lookupOrThrow(key)
165 //?} else {
166 /*return !(server.registryAccess().registryOrThrow(key)
167 *///?}
168 .containsKey(ResourceKey.create(key, InfinityMethods.getId(name))));
169 }
170
171 boolean isOverworldLike() {
172 return (type_alike.equals("minecraft:overworld")) || (type_alike.equals("minecraft:large_biomes"))
173 || (type_alike.equals("minecraft:amplified")) || (type_alike.equals("infinity:whack"));
174 }
175
176 boolean hasCeiling() {
177 return ((type_alike.equals("minecraft:nether")) || (type_alike.equals("minecraft:caves")) || (type_alike.equals("infinity:tangled")));
178 }
179
180 CompoundTag randomDimensionGenerator() {
181 CompoundTag res = new CompoundTag();
182 String type = PROVIDER.randomName(random, ConfigType.GENERATOR_TYPES);
183 res.putString("type", type);
184 switch (type) {
185 case "minecraft:flat" -> {
186 res.put("settings", randomSuperflatSettings());
187 return res;
188 }
189 case "minecraft:noise" -> {
190 res.put("biome_source", randomBiomeSource());
191 res.putString("settings", randomNoiseSettings());
192 res.putLong("seed", numericId ^ server.overworld().getSeed());
193 return res;
194 }
195 default -> {
196 return res;
197 }
198 }
199 }
200
201 CompoundTag superflatLayer(int h, String block) {
202 CompoundTag res = new CompoundTag();
203 res.putInt("height", h);
204 res.putString("block", block);
205 return res;
206 }
207
208 CompoundTag randomSuperflatSettings() {
209 CompoundTag res = new CompoundTag();
210 ListTag layers = new ListTag();
211 String biome = randomBiome();
212 String block = "minecraft:air";
213 int layer_count = Math.min(64, 1 + (int) Math.floor(random.nextExponential() * 2));
214 int heightLeft = height;
215 for (int i = 0; i < layer_count; i++) {
216 int layerHeight = Math.min(heightLeft, 1 + (int) Math.floor(random.nextExponential() * 4));
217 heightLeft -= layerHeight;
218 block = PROVIDER.randomName(random, ConfigType.FULL_BLOCKS_WG);
219 layers.add(superflatLayer(layerHeight, block));
220 if (heightLeft <= 1) {
221 break;
222 }
223 }
224 if (random.nextBoolean()) {
225 block = PROVIDER.randomName(random, ConfigType.TOP_BLOCKS);
226 layers.add(superflatLayer(1, block));
227 }
228 res.putString("biome", biome);
229 res.put("layers", layers);
230 res.putBoolean("lakes", random.nextBoolean());
231 res.putBoolean("features", random.nextBoolean());
232 top_blocks.put(biome, NbtUtils.nameToElement(block));
233 underwater.put(biome, NbtUtils.nameToElement(block));
234 return res;
235 }
236
237 CompoundTag randomBiomeSource() {
238 CompoundTag res = new CompoundTag();
239 String type = PROVIDER.randomName(random, ConfigType.BIOME_SOURCE_TYPES);
240 res.putString("type",type);
241 switch (type) {
242 case "minecraft:the_end" -> {
243 return res;
244 }
245 case "minecraft:checkerboard" -> {
246 res.put("biomes", randomBiomesCheckerboard());
247 res.putInt("scale", Math.min(62, (int) Math.floor(random.nextExponential() * 2)));
248 return res;
249 }
250 case "minecraft:multi_noise" -> {
251 String preset = PROVIDER.randomName(random, ConfigType.MULTINOISE_PRESETS);
252 if (preset.equals("none") || hasCeiling()) res.put("biomes", randomBiomes());
253 else {
254 res.putString("preset", preset.replace("_", ":"));
255 addPresetBiomes(preset);
256 }
257 return res;
258 }
259 case "minecraft:fixed" -> res.putString("biome", randomBiome());
260 }
261 return res;
262 }
263
264 void addPresetBiomes(String preset) {
265 TagKey<Biome> tag = preset.equals("overworld") ? BiomeTags.IS_OVERWORLD : BiomeTags.IS_NETHER;
266 Registry<Biome> r = VersionMethods.getRegistry(server.registryAccess(), Registries.BIOME);
267 r.registryKeySet().forEach(key -> {
268 if (!Objects.equals(key.location().getNamespace(), "infinity")) {
269 if (r.get(key) != null && r.wrapAsHolder(VersionMethods.getFromRegistry(r, key)).is(tag)) vanilla_biomes.add(key.location().toString());
270 }
271 });
272 }
273
274 int getBiomeCount() {
275 return random.nextInt(2, Mth.clamp(RandomProvider.ruleInt("maxBiomeCount"), 2, 10));
276 }
277
278 ListTag randomBiomesCheckerboard() {
279 ListTag res = new ListTag();
280 int biome_count = getBiomeCount();
281 for (int i = 0; i < biome_count; i++) {
282 res.add(StringTag.valueOf(randomBiome()));
283 }
284 return res;
285 }
286 ListTag randomBiomes() {
287 ListTag res = new ListTag();
288 int biome_count = getBiomeCount();
289 for (int i = 0; i < biome_count; i++) {
290 CompoundTag element = new CompoundTag();
291 element.putString("biome", randomBiome());
292 element.put("parameters", randomMultiNoiseParameters());
293 res.add(element);
294 }
295 return res;
296 }
297
298 CompoundTag randomMultiNoiseParameters() {
299 CompoundTag res = new CompoundTag();
300 res.put("temperature", randomMultiNoiseParameter());
301 res.put("humidity", randomMultiNoiseParameter());
302 res.put("continentalness", randomMultiNoiseParameter());
303 res.put("erosion", randomMultiNoiseParameter());
304 res.put("weirdness", DoubleTag.valueOf(0));
305 res.put("depth", DoubleTag.valueOf(0));
306 res.put("offset", DoubleTag.valueOf(0));
307 return res;
308 }
309
310 Tag randomMultiNoiseParameter() {
311 return DoubleTag.valueOf((random.nextDouble()-0.5)*2);
312 }
313
314 String randomBiome() {
315 String biome;
316 if (!hasCeiling() && !PROVIDER.roll(random, "use_random_biome")) {
317 biome = PROVIDER.randomName(random, ConfigType.BIOMES);
318 vanilla_biomes.add(biome);
319 }
320 else {
321 long id = InfinityMethods.getRandomSeed(random);
322 random_biome_ids.add(id);
323 biome = "infinity:biome_" + id;
324 }
325 return biome;
326 }
327
328 String randomNoiseSettings() {
329 RandomNoisePreset preset = new RandomNoisePreset(this);
330 return preset.fullname;
331 }
332
333 void addStructures(RandomBiome b) {
334 int maxCount = RandomProvider.ruleInt("maxStructureCount");
335 Set<String> temp = new HashSet<>();
336 if (maxCount > 0) {
337 maxCount = Math.min(maxCount, 8);
338 int numstructures = random.nextInt(1, maxCount + 1);
339 for (int i = 0; i < numstructures; i++) {
340 addStructure(new RandomStructure(random.nextInt(), b), temp);
341 }
342 }
343 if (PROVIDER.roll( random, "text")) {
344 addStructure(new RandomText(random.nextInt(), b), temp);
345 }
346 }
347 void addStructure(RandomStructure s, Set<String> temp) {
348 if (!temp.contains(s.name)) {
349 temp.add(s.name);
350 s.save();
351 if (!structure_ids.containsKey(s.type)) structure_ids.put(s.type, new ArrayList<>());
352 structure_ids.get(s.type).add(s.fullname);
353 }
354 }
355
356 void writeTags(String rootPath) {
357 String path = rootPath + "/data/minecraft/tags/worldgen/structure";
358 try {
359 Files.createDirectories(Paths.get(path));
360 } catch (IOException e) {
361 throw new RuntimeException(e);
362 }
363 CompoundTag dictionary = CommonIO.read(InfinityMod.utilPath + "/structure_tags.json");
364 Map<String, ListTag> tags = new HashMap<>();
365 for (String s : structure_ids.keySet()) for (String ss : NbtUtils.keys(dictionary)) if (s.contains(ss)) {
366 for (Tag e : (ListTag) Objects.requireNonNull(dictionary.get(ss))) {
367 String t = NbtUtils.getAsString(e);
368 if (!tags.containsKey(t)) tags.put(t, new ListTag());
369 structure_ids.get(s).forEach(fullname -> tags.get(t).add(StringTag.valueOf(fullname)));
370 }
371 }
372 for (String t : tags.keySet()) {
373 CompoundTag compound = new CompoundTag();
374 compound.putBoolean("replace", false);
375 compound.put("values", tags.get(t));
376 CommonIO.write(compound, path, t + ".json");
377 }
378 }
379}