package net.minecraft.util.datafix.fixes; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.collect.ImmutableMap.Builder; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.serialization.Codec; import com.mojang.serialization.Dynamic; import com.mojang.serialization.DynamicLike; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.OptionalDynamic; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableInt; public class WorldGenSettingsFix extends DataFix { private static final String VILLAGE = "minecraft:village"; private static final String DESERT_PYRAMID = "minecraft:desert_pyramid"; private static final String IGLOO = "minecraft:igloo"; private static final String JUNGLE_TEMPLE = "minecraft:jungle_pyramid"; private static final String SWAMP_HUT = "minecraft:swamp_hut"; private static final String PILLAGER_OUTPOST = "minecraft:pillager_outpost"; private static final String END_CITY = "minecraft:endcity"; private static final String WOODLAND_MANSION = "minecraft:mansion"; private static final String OCEAN_MONUMENT = "minecraft:monument"; private static final ImmutableMap DEFAULTS = ImmutableMap.builder() .put("minecraft:village", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 8, 10387312)) .put("minecraft:desert_pyramid", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 8, 14357617)) .put("minecraft:igloo", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 8, 14357618)) .put("minecraft:jungle_pyramid", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 8, 14357619)) .put("minecraft:swamp_hut", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 8, 14357620)) .put("minecraft:pillager_outpost", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 8, 165745296)) .put("minecraft:monument", new WorldGenSettingsFix.StructureFeatureConfiguration(32, 5, 10387313)) .put("minecraft:endcity", new WorldGenSettingsFix.StructureFeatureConfiguration(20, 11, 10387313)) .put("minecraft:mansion", new WorldGenSettingsFix.StructureFeatureConfiguration(80, 20, 10387319)) .build(); public WorldGenSettingsFix(Schema outputSchema) { super(outputSchema, true); } @Override protected TypeRewriteRule makeRule() { return this.fixTypeEverywhereTyped( "WorldGenSettings building", this.getInputSchema().getType(References.WORLD_GEN_SETTINGS), typed -> typed.update(DSL.remainderFinder(), WorldGenSettingsFix::fix) ); } private static Dynamic noise(long seed, DynamicLike data, Dynamic settings, Dynamic biomeNoise) { return data.createMap( ImmutableMap.of( data.createString("type"), data.createString("minecraft:noise"), data.createString("biome_source"), biomeNoise, data.createString("seed"), data.createLong(seed), data.createString("settings"), settings ) ); } private static Dynamic vanillaBiomeSource(Dynamic data, long seed, boolean legacyBiomeInitLayer, boolean largeBiomes) { Builder, Dynamic> builder = ImmutableMap., Dynamic>builder() .put(data.createString("type"), data.createString("minecraft:vanilla_layered")) .put(data.createString("seed"), data.createLong(seed)) .put(data.createString("large_biomes"), data.createBoolean(largeBiomes)); if (legacyBiomeInitLayer) { builder.put(data.createString("legacy_biome_init_layer"), data.createBoolean(legacyBiomeInitLayer)); } return data.createMap(builder.build()); } private static Dynamic fix(Dynamic data) { DynamicOps dynamicOps = data.getOps(); long l = data.get("RandomSeed").asLong(0L); Optional optional = data.get("generatorName").asString().map(string -> string.toLowerCase(Locale.ROOT)).result(); Optional optional2 = (Optional)data.get("legacy_custom_options") .asString() .result() .map(Optional::of) .orElseGet(() -> optional.equals(Optional.of("customized")) ? data.get("generatorOptions").asString().result() : Optional.empty()); boolean bl = false; Dynamic dynamic; if (optional.equals(Optional.of("customized"))) { dynamic = defaultOverworld(data, l); } else if (optional.isEmpty()) { dynamic = defaultOverworld(data, l); } else { String bl6 = (String)optional.get(); switch (bl6) { case "flat": OptionalDynamic optionalDynamic = data.get("generatorOptions"); Map, Dynamic> map = fixFlatStructures(dynamicOps, optionalDynamic); dynamic = data.createMap( ImmutableMap.of( data.createString("type"), data.createString("minecraft:flat"), data.createString("settings"), data.createMap( ImmutableMap.of( data.createString("structures"), data.createMap(map), data.createString("layers"), (Dynamic)optionalDynamic.get("layers") .result() .orElseGet( () -> data.createList( Stream.of( data.createMap(ImmutableMap.of(data.createString("height"), data.createInt(1), data.createString("block"), data.createString("minecraft:bedrock"))), data.createMap(ImmutableMap.of(data.createString("height"), data.createInt(2), data.createString("block"), data.createString("minecraft:dirt"))), data.createMap( ImmutableMap.of(data.createString("height"), data.createInt(1), data.createString("block"), data.createString("minecraft:grass_block")) ) ) ) ), data.createString("biome"), data.createString(optionalDynamic.get("biome").asString("minecraft:plains")) ) ) ) ); break; case "debug_all_block_states": dynamic = data.createMap(ImmutableMap.of(data.createString("type"), data.createString("minecraft:debug"))); break; case "buffet": OptionalDynamic optionalDynamic2 = data.get("generatorOptions"); OptionalDynamic optionalDynamic3 = optionalDynamic2.get("chunk_generator"); Optional optional3 = optionalDynamic3.get("type").asString().result(); Dynamic dynamic2; if (Objects.equals(optional3, Optional.of("minecraft:caves"))) { dynamic2 = data.createString("minecraft:caves"); bl = true; } else if (Objects.equals(optional3, Optional.of("minecraft:floating_islands"))) { dynamic2 = data.createString("minecraft:floating_islands"); } else { dynamic2 = data.createString("minecraft:overworld"); } Dynamic dynamic3 = (Dynamic)optionalDynamic2.get("biome_source") .result() .orElseGet(() -> data.createMap(ImmutableMap.of(data.createString("type"), data.createString("minecraft:fixed")))); Dynamic dynamic4; if (dynamic3.get("type").asString().result().equals(Optional.of("minecraft:fixed"))) { String string = (String)dynamic3.get("options") .get("biomes") .asStream() .findFirst() .flatMap(dynamicx -> dynamicx.asString().result()) .orElse("minecraft:ocean"); dynamic4 = dynamic3.remove("options").set("biome", data.createString(string)); } else { dynamic4 = dynamic3; } dynamic = noise(l, data, dynamic2, dynamic4); break; default: boolean bl2 = ((String)optional.get()).equals("default"); boolean bl3 = ((String)optional.get()).equals("default_1_1") || bl2 && data.get("generatorVersion").asInt(0) == 0; boolean bl4 = ((String)optional.get()).equals("amplified"); boolean bl5 = ((String)optional.get()).equals("largebiomes"); dynamic = noise(l, data, data.createString(bl4 ? "minecraft:amplified" : "minecraft:overworld"), vanillaBiomeSource(data, l, bl3, bl5)); } } boolean bl6 = data.get("MapFeatures").asBoolean(true); boolean bl7 = data.get("BonusChest").asBoolean(false); Builder builder = ImmutableMap.builder(); builder.put(dynamicOps.createString("seed"), dynamicOps.createLong(l)); builder.put(dynamicOps.createString("generate_features"), dynamicOps.createBoolean(bl6)); builder.put(dynamicOps.createString("bonus_chest"), dynamicOps.createBoolean(bl7)); builder.put(dynamicOps.createString("dimensions"), vanillaLevels(data, l, dynamic, bl)); optional2.ifPresent(string -> builder.put(dynamicOps.createString("legacy_custom_options"), dynamicOps.createString(string))); return new Dynamic<>(dynamicOps, dynamicOps.createMap(builder.build())); } protected static Dynamic defaultOverworld(Dynamic data, long seed) { return noise(seed, data, data.createString("minecraft:overworld"), vanillaBiomeSource(data, seed, false, false)); } protected static T vanillaLevels(Dynamic data, long seed, Dynamic generator, boolean caves) { DynamicOps dynamicOps = data.getOps(); return dynamicOps.createMap( ImmutableMap.of( dynamicOps.createString("minecraft:overworld"), dynamicOps.createMap( ImmutableMap.of( dynamicOps.createString("type"), dynamicOps.createString("minecraft:overworld" + (caves ? "_caves" : "")), dynamicOps.createString("generator"), generator.getValue() ) ), dynamicOps.createString("minecraft:the_nether"), dynamicOps.createMap( ImmutableMap.of( dynamicOps.createString("type"), dynamicOps.createString("minecraft:the_nether"), dynamicOps.createString("generator"), noise( seed, data, data.createString("minecraft:nether"), data.createMap( ImmutableMap.of( data.createString("type"), data.createString("minecraft:multi_noise"), data.createString("seed"), data.createLong(seed), data.createString("preset"), data.createString("minecraft:nether") ) ) ) .getValue() ) ), dynamicOps.createString("minecraft:the_end"), dynamicOps.createMap( ImmutableMap.of( dynamicOps.createString("type"), dynamicOps.createString("minecraft:the_end"), dynamicOps.createString("generator"), noise( seed, data, data.createString("minecraft:end"), data.createMap(ImmutableMap.of(data.createString("type"), data.createString("minecraft:the_end"), data.createString("seed"), data.createLong(seed))) ) .getValue() ) ) ) ); } private static Map, Dynamic> fixFlatStructures(DynamicOps ops, OptionalDynamic generatorOptions) { MutableInt mutableInt = new MutableInt(32); MutableInt mutableInt2 = new MutableInt(3); MutableInt mutableInt3 = new MutableInt(128); MutableBoolean mutableBoolean = new MutableBoolean(false); Map map = Maps.newHashMap(); if (generatorOptions.result().isEmpty()) { mutableBoolean.setTrue(); map.put("minecraft:village", DEFAULTS.get("minecraft:village")); } generatorOptions.get("structures") .flatMap(Dynamic::getMapValues) .ifSuccess( map2 -> map2.forEach( (dynamic, dynamic2) -> dynamic2.getMapValues() .result() .ifPresent( map2x -> map2x.forEach( (dynamic2x, dynamic3) -> { String string = dynamic.asString(""); String string2 = dynamic2x.asString(""); String string3 = dynamic3.asString(""); if ("stronghold".equals(string)) { mutableBoolean.setTrue(); switch (string2) { case "distance": mutableInt.setValue(getInt(string3, mutableInt.getValue(), 1)); return; case "spread": mutableInt2.setValue(getInt(string3, mutableInt2.getValue(), 1)); return; case "count": mutableInt3.setValue(getInt(string3, mutableInt3.getValue(), 1)); return; } } else { switch (string2) { case "distance": switch (string) { case "village": setSpacing(map, "minecraft:village", string3, 9); return; case "biome_1": setSpacing(map, "minecraft:desert_pyramid", string3, 9); setSpacing(map, "minecraft:igloo", string3, 9); setSpacing(map, "minecraft:jungle_pyramid", string3, 9); setSpacing(map, "minecraft:swamp_hut", string3, 9); setSpacing(map, "minecraft:pillager_outpost", string3, 9); return; case "endcity": setSpacing(map, "minecraft:endcity", string3, 1); return; case "mansion": setSpacing(map, "minecraft:mansion", string3, 1); return; default: return; } case "separation": if ("oceanmonument".equals(string)) { WorldGenSettingsFix.StructureFeatureConfiguration structureFeatureConfiguration = (WorldGenSettingsFix.StructureFeatureConfiguration)map.getOrDefault( "minecraft:monument", DEFAULTS.get("minecraft:monument") ); int i = getInt(string3, structureFeatureConfiguration.separation, 1); map.put( "minecraft:monument", new WorldGenSettingsFix.StructureFeatureConfiguration(i, structureFeatureConfiguration.separation, structureFeatureConfiguration.salt) ); } return; case "spacing": if ("oceanmonument".equals(string)) { setSpacing(map, "minecraft:monument", string3, 1); } return; } } } ) ) ) ); Builder, Dynamic> builder = ImmutableMap.builder(); builder.put( generatorOptions.createString("structures"), generatorOptions.createMap( (Map, ? extends Dynamic>)map.entrySet() .stream() .collect( Collectors.toMap( entry -> generatorOptions.createString((String)entry.getKey()), entry -> ((WorldGenSettingsFix.StructureFeatureConfiguration)entry.getValue()).serialize(ops) ) ) ) ); if (mutableBoolean.isTrue()) { builder.put( generatorOptions.createString("stronghold"), generatorOptions.createMap( ImmutableMap.of( generatorOptions.createString("distance"), generatorOptions.createInt(mutableInt.getValue()), generatorOptions.createString("spread"), generatorOptions.createInt(mutableInt2.getValue()), generatorOptions.createString("count"), generatorOptions.createInt(mutableInt3.getValue()) ) ) ); } return builder.build(); } private static int getInt(String string, int defaultValue) { return NumberUtils.toInt(string, defaultValue); } private static int getInt(String string, int defaultValue, int minValue) { return Math.max(minValue, getInt(string, defaultValue)); } private static void setSpacing(Map map, String structure, String spacing, int minValue) { WorldGenSettingsFix.StructureFeatureConfiguration structureFeatureConfiguration = (WorldGenSettingsFix.StructureFeatureConfiguration)map.getOrDefault( structure, DEFAULTS.get(structure) ); int i = getInt(spacing, structureFeatureConfiguration.spacing, minValue); map.put(structure, new WorldGenSettingsFix.StructureFeatureConfiguration(i, structureFeatureConfiguration.separation, structureFeatureConfiguration.salt)); } static final class StructureFeatureConfiguration { public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( Codec.INT.fieldOf("spacing").forGetter(structureFeatureConfiguration -> structureFeatureConfiguration.spacing), Codec.INT.fieldOf("separation").forGetter(structureFeatureConfiguration -> structureFeatureConfiguration.separation), Codec.INT.fieldOf("salt").forGetter(structureFeatureConfiguration -> structureFeatureConfiguration.salt) ) .apply(instance, WorldGenSettingsFix.StructureFeatureConfiguration::new) ); final int spacing; final int separation; final int salt; public StructureFeatureConfiguration(int spacing, int separation, int salt) { this.spacing = spacing; this.separation = separation; this.salt = salt; } public Dynamic serialize(DynamicOps ops) { return new Dynamic<>(ops, (T)CODEC.encodeStart(ops, this).result().orElse(ops.emptyMap())); } } }