/******************************************************************************* * Copyright 2014-2019, the Biomes O' Plenty Team * * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License. * * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/. ******************************************************************************/ package biomesoplenty.common.biome; import biomesoplenty.api.biome.BOPBiomes; import biomesoplenty.api.enums.BOPClimates; import biomesoplenty.common.util.biome.BiomeUtil; import biomesoplenty.common.util.config.JsonUtil; import biomesoplenty.core.BiomesOPlenty; import biomesoplenty.init.ModBiomes; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.gson.reflect.TypeToken; import net.minecraft.util.RegistryKey; import net.minecraft.util.ResourceLocation; import net.minecraft.world.biome.Biome; import net.minecraftforge.fml.loading.FMLPaths; import net.minecraftforge.registries.ForgeRegistries; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nullable; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.function.Consumer; public class BiomeRegistry { private static final String CONFIG_FILE_NAME = "biomes.json"; private static Map> deferrances = Maps.newHashMap(); public static void deferStandardRegistration(BiomeTemplate biome, String name) { defer(RegistrationType.STANDARD_BIOME, new StandardBiomeRegistrationData(biome, name)); } public static void deferTechnicalBiomeRegistration(BiomeTemplate biome, String name) { defer(RegistrationType.TECHNICAL_BIOME, new ToggleableStandardBiomeRegistrationData(biome, name, true)); } public static void deferSubBiomeRegistration(RegistryKey parent, RegistryKey child, int weight, float rarity) { // Don't register sub biome if the parent or child don't exist if (!BiomeUtil.exists(parent) || !BiomeUtil.exists(child)) { return; } defer(RegistrationType.SUB_BIOME, new SubBiomeRegistrationData(parent, child, weight, rarity)); } public static void deferIslandBiomeRegistration(RegistryKey key, BOPClimates climate, int weight) { if (!BiomeUtil.exists(key)) return; defer(RegistrationType.ISLAND_BIOME, new SingleClimateRegistrationData(key, climate, weight)); } public static void deferVanillaBiomeRegistration(RegistryKey key, BOPClimates climate, int weight) { if (!BiomeUtil.exists(key)) return; defer(RegistrationType.VANILLA_BIOME, new SingleClimateRegistrationData(key, climate, weight)); } public static void configureStandardBiomes() { List standardRegistrations = deferrances.get(RegistrationType.STANDARD_BIOME); TreeMap defaultEntries = Maps.newTreeMap(); Map regDataMap = Maps.newHashMap(); for (DeferredRegistration registration : standardRegistrations) { StandardBiomeRegistrationData regData = registration.regData; // Ignore biomes which don't have any weights set by default if (regData.getMetadata().hasWeights()) { String biomeName = new ResourceLocation(BiomesOPlenty.MOD_ID, regData.getName()).toString(); Pair primaryWeight = regData.getPrimaryWeight(); defaultEntries.put(biomeName, new BiomeConfigData.WeightedBiomeEntry(primaryWeight.getValue())); regDataMap.put(biomeName, registration.regData); } } BiomeConfigData defaultConfigData = new BiomeConfigData(); defaultConfigData.standardBiomeWeights = defaultEntries; BiomeConfigData configData = getConfigData(defaultConfigData); TreeMap revisedStandardBiomeWeights = Maps.newTreeMap(defaultEntries); // Merge the config file with the default values for (Map.Entry biomeEntry : configData.standardBiomeWeights.entrySet()) { if (revisedStandardBiomeWeights.containsKey(biomeEntry.getKey())) { revisedStandardBiomeWeights.put(biomeEntry.getKey(), biomeEntry.getValue()); } } // Write back to the config file configData.standardBiomeWeights = revisedStandardBiomeWeights; JsonUtil.writeFile(getConfigFile(), configData); for (Map.Entry biomeEntry : configData.standardBiomeWeights.entrySet()) { String name = biomeEntry.getKey(); BiomeConfigData.WeightedBiomeEntry weight = biomeEntry.getValue(); // Replace the default weight map for this biome with those from the config file if (regDataMap.containsKey(name)) { StandardBiomeRegistrationData registrationData = regDataMap.get(name); registrationData.setPrimaryWeight(weight.weight); } } } public static void configureTechnicalBiomes() { List biomeRegistrations = deferrances.get(RegistrationType.TECHNICAL_BIOME); TreeMap defaultBiomeEntries = Maps.newTreeMap(); for (DeferredRegistration registration : biomeRegistrations) { ToggleableStandardBiomeRegistrationData regData = registration.regData; String biomeName = new ResourceLocation(BiomesOPlenty.MOD_ID, regData.getName()).toString(); defaultBiomeEntries.put(biomeName, new BiomeConfigData.ToggleableBiomeEntry(true)); } BiomeConfigData defaultConfigData = new BiomeConfigData(); defaultConfigData.technicalBiomeEntries = defaultBiomeEntries; BiomeConfigData configData = getConfigData(defaultConfigData); TreeMap revisedBiomeEntries = Maps.newTreeMap(defaultBiomeEntries); // Merge the config file with the default values for (Map.Entry biomeEntry : configData.technicalBiomeEntries.entrySet()) { if (revisedBiomeEntries.containsKey(biomeEntry.getKey())) { revisedBiomeEntries.put(biomeEntry.getKey(), biomeEntry.getValue()); } } // Write back to the config file configData.technicalBiomeEntries = revisedBiomeEntries; JsonUtil.writeFile(getConfigFile(), configData); for (DeferredRegistration registration : biomeRegistrations) { ToggleableStandardBiomeRegistrationData regData = registration.regData; String biomeName = new ResourceLocation(BiomesOPlenty.MOD_ID, regData.getName()).toString(); if (revisedBiomeEntries.containsKey(biomeName)) { BiomeConfigData.ToggleableBiomeEntry entry = revisedBiomeEntries.get(biomeName); if (!entry.enabled) { registration.regData.setEnabled(false); } } } } public static void configureSubBiomes() { List subBiomeRegistrations = deferrances.get(RegistrationType.SUB_BIOME); TreeMap defaultSubBiomeEntries = Maps.newTreeMap(); Map regDataMap = Maps.newHashMap(); for (DeferredRegistration registration : subBiomeRegistrations) { SubBiomeRegistrationData regData = registration.regData; String biomeName = registration.regData.getChild().getRegistryName().toString(); defaultSubBiomeEntries.put(biomeName, new BiomeConfigData.SubBiomeEntry(regData.getWeight(), regData.getRarity())); regDataMap.put(biomeName, registration.regData); } BiomeConfigData defaultConfigData = new BiomeConfigData(); defaultConfigData.subBiomeEntries = defaultSubBiomeEntries; BiomeConfigData configData = getConfigData(defaultConfigData); TreeMap revisedSubBiomeEntries = Maps.newTreeMap(defaultSubBiomeEntries); // Merge the config file with the default values for (Map.Entry biomeEntry : configData.subBiomeEntries.entrySet()) { if (revisedSubBiomeEntries.containsKey(biomeEntry.getKey())) { revisedSubBiomeEntries.put(biomeEntry.getKey(), biomeEntry.getValue()); } } // Write back to the config file configData.subBiomeEntries = revisedSubBiomeEntries; JsonUtil.writeFile(getConfigFile(), configData); for (Map.Entry biomeEntry : configData.subBiomeEntries.entrySet()) { String name = biomeEntry.getKey(); BiomeConfigData.SubBiomeEntry subBiomeEntry = biomeEntry.getValue(); // Replace the default values for this biome with those from the config file if (regDataMap.containsKey(name)) { SubBiomeRegistrationData registrationData = regDataMap.get(name); registrationData.setWeight(subBiomeEntry.weight); registrationData.setRarity(subBiomeEntry.rarity); } } } public static void configureIslandBiomes() { List biomeRegistrations = deferrances.get(RegistrationType.ISLAND_BIOME); TreeMap defaultBiomeEntries = Maps.newTreeMap(); for (DeferredRegistration registration : biomeRegistrations) { SingleClimateRegistrationData regData = registration.regData; String biomeName = regData.getBiome().getRegistryName().toString(); defaultBiomeEntries.put(biomeName, new BiomeConfigData.ToggleableBiomeEntry(true)); } BiomeConfigData defaultConfigData = new BiomeConfigData(); defaultConfigData.islandBiomeEntries = defaultBiomeEntries; BiomeConfigData configData = getConfigData(defaultConfigData); TreeMap revisedBiomeEntries = Maps.newTreeMap(defaultBiomeEntries); // Merge the config file with the default values for (Map.Entry biomeEntry : configData.islandBiomeEntries.entrySet()) { if (revisedBiomeEntries.containsKey(biomeEntry.getKey())) { revisedBiomeEntries.put(biomeEntry.getKey(), biomeEntry.getValue()); } } // Write back to the config file configData.islandBiomeEntries = revisedBiomeEntries; JsonUtil.writeFile(getConfigFile(), configData); for (DeferredRegistration registration : biomeRegistrations) { SingleClimateRegistrationData regData = registration.regData; String biomeName = regData.getBiome().getRegistryName().toString(); if (revisedBiomeEntries.containsKey(biomeName)) { BiomeConfigData.ToggleableBiomeEntry entry = revisedBiomeEntries.get(biomeName); if (!entry.enabled) { registration.regData.setWeight(0); } } } } public static void configureVanillaBiomes() { List biomeRegistrations = deferrances.get(RegistrationType.VANILLA_BIOME); TreeMap defaultBiomeEntries = Maps.newTreeMap(); Map regDataMap = Maps.newHashMap(); for (DeferredRegistration registration : biomeRegistrations) { SingleClimateRegistrationData regData = registration.regData; String biomeName = registration.regData.getBiome().getRegistryName().toString(); defaultBiomeEntries.put(biomeName, new BiomeConfigData.WeightedBiomeEntry(regData.getWeight())); regDataMap.put(biomeName, registration.regData); } BiomeConfigData defaultConfigData = new BiomeConfigData(); defaultConfigData.vanillaBiomeEntries = defaultBiomeEntries; BiomeConfigData configData = getConfigData(defaultConfigData); TreeMap revisedBiomeEntries = Maps.newTreeMap(defaultBiomeEntries); // Merge the config file with the default values for (Map.Entry biomeEntry : configData.vanillaBiomeEntries.entrySet()) { if (revisedBiomeEntries.containsKey(biomeEntry.getKey())) { revisedBiomeEntries.put(biomeEntry.getKey(), biomeEntry.getValue()); } } // Write back to the config file configData.vanillaBiomeEntries = revisedBiomeEntries; JsonUtil.writeFile(getConfigFile(), configData); for (Map.Entry biomeEntry : configData.vanillaBiomeEntries.entrySet()) { String name = biomeEntry.getKey(); BiomeConfigData.WeightedBiomeEntry islandBiomeEntry = biomeEntry.getValue(); // Replace the default values for this biome with those from the config file if (regDataMap.containsKey(name)) { SingleClimateRegistrationData registrationData = regDataMap.get(name); registrationData.setWeight(islandBiomeEntry.weight); } } } private static File getConfigDirFile() { Path configPath = FMLPaths.CONFIGDIR.get(); Path bopConfigPath = Paths.get(configPath.toAbsolutePath().toString(), "biomesoplenty"); return bopConfigPath.toFile(); } private static File getConfigFile() { return new File(getConfigDirFile(), CONFIG_FILE_NAME); } private static BiomeConfigData getConfigData(BiomeConfigData defaultConfigData) { BiomeConfigData configData = JsonUtil.getOrCreateConfigFile(getConfigDirFile(), CONFIG_FILE_NAME, defaultConfigData, new TypeToken(){}.getType()); return configData; } private static void defer(RegistrationType type, T data) { if (!deferrances.containsKey(type)) deferrances.put(type, Lists.newArrayList()); List list = deferrances.get(type); list.add(new DeferredRegistration(type.regFunc, data)); } public static void finalizeRegistrations(RegistrationType type) { if (!deferrances.containsKey(type)) return; if (type == RegistrationType.SUB_BIOME) { Set> children = Sets.newHashSet(); deferrances.get(RegistrationType.SUB_BIOME).forEach((reg) -> { RegistryKey biome = ((SubBiomeRegistrationData)reg.regData).getChild(); if (children.contains(biome)) { throw new RuntimeException(String.format("Sub biome %s cannot be added to multiple parents", biome.getRegistryName().toString())); } children.add(biome); }); } for (DeferredRegistration reg : deferrances.get(type)) { reg.register(); } } public enum RegistrationType { STANDARD_BIOME((StandardBiomeRegistrationData data) -> { Biome biome = data.getBiome(); BiomeMetadata metadata = data.getMetadata(); String name = data.getName(); // Don't register biomes with their weight set to 0, that normally have weights that are non-zero if (!metadata.getWeightMap().isEmpty() && (data.weightMap.isEmpty() || data.weightMap.entrySet().stream().allMatch((entry) -> entry.getValue().equals(0)))) { BiomesOPlenty.logger.debug("Weights absent for " + data.getName() + ", disabling..."); return; } biome.setRegistryName(name); ForgeRegistries.BIOMES.register(biome); for (Map.Entry entry : data.getWeights().entrySet()) { if (entry != null && entry.getValue() > 0) { BOPClimates climate = entry.getKey(); int weight = entry.getValue(); BiomesOPlenty.logger.debug(String.format("%s weight set to %d for climate %s", name, weight, climate.name())); climate.addBiome(weight, BiomeUtil.createKey(data.getBiome())); } } if (data.getMetadata() != null) { ModBiomes.biomeMetadata.put(BiomeUtil.createKey(data.getBiome()), data.getMetadata()); } }), TECHNICAL_BIOME((ToggleableStandardBiomeRegistrationData data) -> { Biome biome = data.getBiome(); String name = data.getName(); if (!data.getEnabled()) { BiomesOPlenty.logger.debug("Technical biome " + data.getName() + " is disabled."); return; } biome.setRegistryName(name); ForgeRegistries.BIOMES.register(biome); if (data.getMetadata() != null) { ModBiomes.biomeMetadata.put(BiomeUtil.createKey(data.getBiome()), data.getMetadata()); } }), SUB_BIOME((SubBiomeRegistrationData data) -> { if (data.getWeight() == 0) { BiomesOPlenty.logger.debug("Weights absent for sub biome" + data.getChild().getRegistryName().toString() + ", disabling..."); return; } String childName = data.getChild().getRegistryName().toString(); BiomesOPlenty.logger.debug(String.format("Sub biome %s weight set to %d", childName, data.getWeight())); ModBiomes.subBiomes.put(BiomeUtil.getBiomeId(data.getParent()), new ModBiomes.WeightedSubBiome(data.getChild(), data.getRarity(), data.getWeight())); }), ISLAND_BIOME((SingleClimateRegistrationData data) -> { if (data.getWeight() == 0) { BiomesOPlenty.logger.debug("Weights absent for island biome" + data.getBiome().getRegistryName().toString() + ", disabling..."); return; } String biomeName = data.getBiome().getRegistryName().toString(); BiomesOPlenty.logger.debug(String.format("Island biome %s weight set to %d for climate %s", biomeName, data.getWeight(), data.getClimate().name())); ModBiomes.islandBiomeIds.add(BiomeUtil.getBiomeId(data.getBiome())); data.getClimate().addIslandBiome(data.getWeight(), data.getBiome()); }), VANILLA_BIOME((SingleClimateRegistrationData data) -> { if (data.getWeight() == 0) { BiomesOPlenty.logger.debug("Weights absent for vanilla biome" + data.getBiome().getRegistryName().toString() + ", disabling..."); return; } data.getClimate().addBiome(data.getWeight(), data.getBiome()); }); public final Consumer regFunc; RegistrationType(Consumer regFunc) { this.regFunc = regFunc; } } private interface IRegistrationData { } private static abstract class TemplateRegistrationData implements IRegistrationData { private final Biome biome; private final BiomeMetadata metadata; public TemplateRegistrationData(BiomeTemplate template) { this.biome = template.build(); this.metadata = template.buildMetadata(); } public TemplateRegistrationData(Biome biome) { this.biome = biome; this.metadata = null; } public Biome getBiome() { return this.biome; } @Nullable public BiomeMetadata getMetadata() { return this.metadata; } } private static class StandardBiomeRegistrationData extends TemplateRegistrationData { private final String name; private Map weightMap; public StandardBiomeRegistrationData(BiomeTemplate biome, String name) { super(biome); this.name = name; this.weightMap = Maps.newHashMap(this.getMetadata().getWeightMap()); this.ensureSingleWeight(); } public String getName() { return this.name; } public ImmutableMap getWeights() { return ImmutableMap.copyOf(this.weightMap); } public int getWeight(BOPClimates climate) { return this.weightMap.get(climate); } public void setWeight(BOPClimates climate, int weight) { this.weightMap.put(climate, weight); this.ensureSingleWeight(); } public Pair getPrimaryWeight() { List> pairs = Lists.newArrayList(); this.weightMap.entrySet().forEach((entry) -> pairs.add(Pair.of(entry.getKey(), entry.getValue()))); return pairs.get(0); } public void setPrimaryWeight(int value) { BOPClimates climate = this.getPrimaryWeight().getKey(); this.setWeight(climate, value); } // This limitation is enforced for config file simplicity, and because we don't need it at this time private void ensureSingleWeight() { if (this.weightMap.size() > 1) { throw new RuntimeException(String.format("%s cannot be assigned to multiple climates!\n%s", new ResourceLocation(BiomesOPlenty.MOD_ID, name).toString(), this.weightMap)); } } } private static class SubBiomeRegistrationData implements IRegistrationData { private final RegistryKey parent; private final RegistryKey child; private int weight; private float rarity; public SubBiomeRegistrationData(RegistryKey parent, RegistryKey child, int weight, float rarity) { this.parent = parent; this.child = child; this.weight = weight; this.rarity = rarity; } public RegistryKey getParent() { return this.parent; } public RegistryKey getChild() { return this.child; } public int getWeight() { return this.weight; } public void setWeight(int weight) { this.weight = weight; } public float getRarity() { return this.rarity; } public void setRarity(float rarity) { this.rarity = rarity; } } private static class SingleClimateRegistrationData implements IRegistrationData { private final BOPClimates climate; private final RegistryKey biome; private int weight; public SingleClimateRegistrationData(RegistryKey biome, BOPClimates climate, int weight) { this.biome = biome; this.climate = climate; this.weight = weight; } public RegistryKey getBiome() { return this.biome; } public BOPClimates getClimate() { return this.climate; } public int getWeight() { return this.weight; } public void setWeight(int weight) { this.weight = weight; } } private static class ToggleableStandardBiomeRegistrationData extends TemplateRegistrationData { private final String name; private boolean enabled; public ToggleableStandardBiomeRegistrationData(BiomeTemplate biome, String name, boolean enabled) { super(biome); this.name = name; this.enabled = enabled; } public String getName() { return this.name; } public boolean getEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } } private static class DeferredRegistration { private final Consumer regFunc; private final T regData; public DeferredRegistration(Consumer regFunc, T regData) { this.regFunc = regFunc; this.regData = regData; } public void register() { this.regFunc.accept(this.regData); } } }