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