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