<|start_filename|>src/main/java/CustomOreGen/Server/NumericOption.java<|end_filename|>
package CustomOreGen.Server;

public class NumericOption extends ConfigOption
{
    private double _value = 0.0D;
    private double _nvalue = 0.0D;
    private double _base = 0.0D;
    private double _range = 0.0D;
    private double _displayBase = Double.NaN;
    private double _displayRange = Double.NaN;
    private double _displayIncr = Double.NaN;

    public NumericOption(String name)
    {
        super(name);
    }

    public Object getValue()
    {
        return Double.valueOf(this._value);
    }

    public boolean setValue(Object value)
    {
        double newVal = 0.0D;

        if (value == null)
        {
            return false;
        }
        else
        {
            if (!Double.TYPE.isInstance(value) && !(value instanceof Double))
            {
                if (value instanceof Number)
                {
                    newVal = ((Number)value).doubleValue();
                }
                else
                {
                    if (!(value instanceof String))
                    {
                        return false;
                    }

                    try
                    {
                        newVal = Double.parseDouble((String)value);
                    }
                    catch (NumberFormatException var5)
                    {
                        return false;
                    }
                }
            }
            else
            {
                newVal = ((Double)value).doubleValue();
            }

            if (newVal >= this.getMin() && newVal <= this.getMax())
            {
                this._value = newVal;
                this._nvalue = this._range == 0.0D ? 0.0D : this.round((newVal - this._base) / this._range);
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public double getMin()
    {
        return this._base;
    }

    public double getMax()
    {
        return this._base + this._range;
    }

    public boolean setLimits(double min, double max)
    {
        if (max < min)
        {
            return false;
        }
        else
        {
            this._base = min;
            this._range = max - min;

            if (this._value < min)
            {
                this._value = min;
                this._nvalue = 0.0D;
            }
            else if (this._value > max)
            {
                this._value = max;
                this._nvalue = 1.0D;
            }

            return true;
        }
    }

    public double getDisplayMin()
    {
        return Double.isNaN(this._displayBase) ? this._base : this._displayBase;
    }

    public double getDisplayMax()
    {
        double range = Double.isNaN(this._displayRange) ? this._range : this._displayRange;
        return this.getDisplayMin() + range;
    }

    public double getDisplayIncr()
    {
        if (Double.isNaN(this._displayIncr))
        {
            double range = Double.isNaN(this._displayRange) ? this._range : this._displayRange;
            return range / 100.0D;
        }
        else
        {
            return this._displayIncr;
        }
    }

    public boolean setDisplayLimits(double min, double max, double incr)
    {
        double r = max - min;

        if (r < 0.0D)
        {
            return false;
        }
        else
        {
            double f = r / incr;

            if (!Double.isInfinite(f) && f >= 1.0D)
            {
                this._displayBase = min;
                this._displayRange = r;
                this._displayIncr = incr;
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public double getDisplayValue()
    {
        double range = Double.isNaN(this._displayRange) ? this._range : this._displayRange;
        return this.round(this.getDisplayMin() + this.getNormalizedDisplayValue() * range);
    }

    public double getNormalizedDisplayValue()
    {
        double range = Double.isNaN(this._displayRange) ? this._range : this._displayRange;

        if (range == 0.0D)
        {
            return 0.0D;
        }
        else
        {
            double factor = Double.isNaN(this._displayIncr) ? 100.0D : range / this._displayIncr;
            return this.round(this._nvalue, factor);
        }
    }

    public boolean setNormalizedDisplayValue(double value)
    {
        if (value >= 0.0D && value <= 1.0D)
        {
            this._nvalue = value;
            this._nvalue = this.getNormalizedDisplayValue();
            this._value = this.round(this._base + this._nvalue * this._range);
            return true;
        }
        else
        {
            return false;
        }
    }

    private double round(double value)
    {
        return (double)Math.round(value * 1000000.0D) / 1000000.0D;
    }

    private double round(double value, double factor)
    {
        factor = Math.min(Math.abs(factor), 1000000.0D);
        double ret = (double)Math.round(value * factor) / factor;
        return ret;
    }
}


<|start_filename|>src/main/java/CustomOreGen/CustomPacketPayloadHandler.java<|end_filename|>
package CustomOreGen;

import java.util.function.Supplier;

import CustomOreGen.CustomPacketPayload.PayloadType;
import CustomOreGen.Client.ClientState;
import CustomOreGen.Client.ClientState.WireframeRenderMode;
import CustomOreGen.Server.ServerState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkDirection;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;

public class CustomPacketPayloadHandler {
	public CustomPacketPayloadHandler() {
		
	}
	
	private static final String PROTOCOL_VERSION = Integer.toString(1);
	private static final String CHANNEL_NAME = "customoregen";
    
	private static final SimpleChannel CHANNEL = buildChannel(CHANNEL_NAME);
	
	private static final SimpleChannel buildChannel(String name) {
		return NetworkRegistry.ChannelBuilder
				.named(new ResourceLocation(CustomOreGenBase.MODID, name))
				.clientAcceptedVersions(PROTOCOL_VERSION::equals)
				.serverAcceptedVersions(PROTOCOL_VERSION::equals)
				.networkProtocolVersion(() -> PROTOCOL_VERSION)
				.simpleChannel();
	}
	
	public static void register()
	{
		CHANNEL.registerMessage(0, CustomPacketPayload.class, CustomPacketPayload::encode, CustomPacketPayload::decode, CustomPacketPayloadHandler::handle);
	}
	
	private static void handle(CustomPacketPayload msg, Supplier<NetworkEvent.Context> ctx) {
		if (ctx.get().getDirection() == NetworkDirection.PLAY_TO_CLIENT) {
			handleClient(msg, ctx);
		} else {
			handleServer(msg, ctx);
		}
	}
	
	private static void handleClient(CustomPacketPayload msg, Supplier<NetworkEvent.Context> ctx)
    {
    	Minecraft mc = Minecraft.getInstance();
    	ClientPlayerEntity player = mc.player;
        if (mc.world != null && ClientState.hasWorldChanged(mc.world))
        {
            ClientState.onWorldChanged(mc.world);
        }
        
        if (msg != null)
        {
            switch (msg.type)
            {
                case DebuggingGeometryData:
                    ClientState.addDebuggingGeometry((GeometryData)msg.data);
                    break;

                case DebuggingGeometryRenderMode:
                    /*String strMode = (String)msg.data;

                    if ("_DISABLE_".equals(strMode))
                    {
                        ClientState.dgEnabled = false;
                        return;
                    }

                    if (strMode != null)
                    {
                        WireframeRenderMode idx = null;
                        for (WireframeRenderMode mode : WireframeRenderMode.values()) {
                        	if (mode.name().equalsIgnoreCase(strMode))
                            {
                                idx = mode;
                                break;
                            }
                        }
                        
                        if (idx != null)
                        {
                            ClientState.dgRenderingMode = idx;
                        }
                        else
                        {
                            player.sendMessage(new StringTextComponent("\u00a7cError: Invalid wireframe mode '" + strMode + "'"));
                        }
                    }
                    else*/
                    {
                        int mode = ClientState.dgRenderingMode == null ? 0 : ClientState.dgRenderingMode.ordinal();
                        mode = (mode + 1) % WireframeRenderMode.values().length;
                        ClientState.dgRenderingMode = WireframeRenderMode.values()[mode];
                    }
                    
                    player.sendMessage(new StringTextComponent("COG Client wireframe mode: " + ClientState.dgRenderingMode.name()));
                    break;

                case DebuggingGeometryReset:
                    ClientState.clearDebuggingGeometry();
                    break;

                case CommandResponse:
                    //player.sendMessage(new StringTextComponent((String)msg.data));
                    break;

                default:
                    throw new RuntimeException("Unhandled client packet type " + msg.type);
            }
        }
    }

	private static void handleServer(CustomPacketPayload msg, Supplier<NetworkEvent.Context> ctx)
    {
    	ServerPlayerEntity player = ctx.get().getSender();
    	World handlerWorld = player.world;
        ServerState.checkIfServerChanged(handlerWorld.getServer(), handlerWorld.getWorldInfo());
        
        if (msg != null)
        {
            switch (msg.type)
            {
                case DebuggingGeometryRequest:
                    GeometryData geometryData = null;

                    if (player.getServer().getPlayerList().canSendCommands(player.getGameProfile()));
                    {
                        geometryData = ServerState.getDebuggingGeometryData((GeometryRequestData)msg.data);
                    }

                    if (geometryData == null)
                    {
                        //sendTo(new CustomPacketPayload(PayloadType.DebuggingGeometryRenderMode, "_DISABLE_"), player);
                    }
                    else
                    {
                        sendTo(new CustomPacketPayload(PayloadType.DebuggingGeometryData, geometryData), player);
                    }

                    break;

                default:
                    throw new RuntimeException("Unhandled server packet type " + msg.type);
            }
        }
    }
	
	/**
	 * Sends a packet to the server.<br>
	 * Must be called Client side. 
	 */
	public static void sendToServer(Object msg)
	{
		//CHANNEL.sendToServer(msg);
	}
	
	/**
	 * Send a packet to a specific player.<br>
	 * Must be called Server side. 
	 */
	public static void sendTo(Object msg, ServerPlayerEntity player)
	{
		/*if (!(player instanceof FakePlayer))
		{
			CHANNEL.sendTo(msg, player.connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
		}*/
	}

	public static void sendToAllClients(Object msg) {
		//CHANNEL.send(PacketDistributor.ALL.noArg(), msg);
	}
}


<|start_filename|>src/main/java/CustomOreGen/Util/WorldHeightScale.java<|end_filename|>
package CustomOreGen.Util;

import net.minecraft.world.World;

public class WorldHeightScale implements HeightScale {

	@Override
	public int getHeight(World world, int x, int z) {
		/*if (world.provider.isSkyColored()) {
			return world.provider.getActualHeight();
		}
		return world.provider.getAverageGroundLevel();*/
		//TODO: this is the best I can find for ground level.
		return (int) world.getWorldInfo().getGenerator().getHorizon(world);
	}

	@Override
	public String getName() {
		return "world";
	}

}


<|start_filename|>src/main/java/CustomOreGen/GeometryRequestData.java<|end_filename|>
package CustomOreGen;

import java.io.Serializable;

import net.minecraft.world.World;

public class GeometryRequestData implements Serializable
{
    public transient World world;
    public int dimensionID;
    public int chunkX;
    public int chunkZ;
    public int batchID;
    private static final long serialVersionUID = 2L;

    public GeometryRequestData() {}

    public GeometryRequestData(World world, int chunkX, int chunkZ, int batchID)
    {
        this.world = world;
        this.dimensionID = world == null ? 0 : world.dimension.getType().getId();
        this.chunkX = chunkX;
        this.chunkZ = chunkZ;
        this.batchID = batchID;
    }
}


<|start_filename|>src/main/java/CustomOreGen/Server/ChoiceOption.java<|end_filename|>
package CustomOreGen.Server;

import java.util.LinkedHashMap;

import CustomOreGen.Util.CIStringMap;
import CustomOreGen.Util.Localization;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

public class ChoiceOption extends ConfigOption
{
    private String _value = null;
    private CIStringMap<String[]> _valueMap = new CIStringMap<String[]>(new LinkedHashMap<String,String[]>());

    public ChoiceOption(String name)
    {
        super(name);
    }

    public Object getValue()
    {
        return this._value;
    }

    public boolean setValue(Object value)
    {
        if (value == null)
        {
            return false;
        }
        else if (!(value instanceof String))
        {
            return false;
        }
        else
        {
            String canonicalValue = this._valueMap.getCanonicalKey((String)value);

            if (canonicalValue == null)
            {
                return false;
            }
            else
            {
                this._value = canonicalValue;
                return true;
            }
        }
    }

    public void addPossibleValue(String value, String displayValue, String description)
    {
        if (value != null)
        {
            this._valueMap.put(value, new String[] {displayValue, description});

            if (this._value == null)
            {
                this._value = value;
            }
        }
    }

    public void removePossibleValue(String value)
    {
        if (value != null)
        {
            if (this._value == value)
            {
                this._value = this.nextPossibleValue();

                if (this._value == value)
                {
                    this._value = null;
                }
            }

            this._valueMap.remove(value);
        }
    }

    public String nextPossibleValue()
    {
        boolean found = false;
        String first = null;
        
        for (String s : this._valueMap.keySet()) {
        	
            if (first == null)
            {
                first = s;
            }

            if (found)
            {
                return s;
            }

            if (s.equals(this._value))
            {
                found = true;
            }
        }

        if (found)
        {
            return first;
        }
        else
        {
            return null;
        }
    }

    public String getDisplayValue()
    {
        String[] display = this._valueMap.get(this._value);
        return display != null && display[0] != null ? display[0] : this._value;
    }

    public String getValueDescription()
    {
        String[] display = this._valueMap.get(this._value);
        return display == null ? null : display[1];
    }

    @OnlyIn(Dist.CLIENT)
    public String getLocalizedValueDescription() {
    	return this.localize("description", this.getValueDescription());
    }

    @OnlyIn(Dist.CLIENT)
    public String getLocalizedDisplayValue() {
    	return this.localize("displayValue", this.getDisplayValue());
    }

    @OnlyIn(Dist.CLIENT)
	private String localize(String key, String value) {
		return Localization.maybeLocalize(this.getName() + "." + this.getValue() + "." + key, value); 
	}
}


<|start_filename|>src/main/java/CustomOreGen/CustomOreGenBase.java<|end_filename|>
package CustomOreGen;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import CustomOreGen.Server.ServerState;
import CustomOreGen.Server.WorldConfig;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.loading.FMLPaths;

@Mod("customoregen")
public class CustomOreGenBase
{
    public static CustomOreGenBase instance;
    
    public static final String MODID = "customoregen";
    public static Logger log = LogManager.getLogger();
    
    public static final String OPTIONS_FILENAME = "CustomOreGen_Options.txt";
	public static final String BASE_CONFIG_FILENAME = "CustomOreGen_Config.xml";
	public static final String DEFAULT_BASE_CONFIG_FILENAME = "CustomOreGen_Config_Default.xml";

    private static int _hasMystcraft = 0;

    public CustomOreGenBase() {
    	instance = this;
    }
    
    public static boolean isClassLoaded(String className)
    {
        try
        {
            CustomOreGenBase.class.getClassLoader().loadClass(className);
            return true;
        }
        catch (ClassNotFoundException var2)
        {
            return false;
        }
    }

    public static void onModPostLoad()
    {
        unpackConfigs();
        hasMystcraft();
    }

    private static void unpackConfigs() {
    	File configPath = getConfigDir();
        File modulesDir = new File(configPath, "modules");
        
        File defaultModulesDir = new File(modulesDir, "default");
        if (defaultModulesDir.exists()) {
        	File[] defaultModules = defaultModulesDir.listFiles();
        	for (File defaultModule : defaultModules) {
        		defaultModule.delete();
        	}
        } else {
        	configPath.mkdir();
        	modulesDir.mkdir();
        	defaultModulesDir.mkdir();
        }
        String[] extraModules = new String[] {
            "AppliedEnergistics.xml",
            "ArsMagica2.xml",
            "BiomesOPlenty.xml",
            "Chisel2.xml",
            "CommonOres.xml",
            "DenseOres.xml",
            "ElectriCraft.xml",
            "EtFuturum.xml",
            "Factorization.xml",
            "FlatBedrock.xml",
            "FlaxbeardsSteamcraft.xml",
            "Forestry.xml",
            "FossilsandArchaeology.xml",
            "Galacticraft.xml",
            "GeoStrata.xml",
            "Gregtech.xml",
            "Gregtech6.xml",
            "ImmersiveEngineering.xml",
            "MagnetiCraft.xml",
            "Mariculture.xml",
            "IndustrialCraft2.xml",
            "Mekanism.xml",
            "Metallurgy4.xml",
            "Mimicry.xml",
            "MineChem.xml",
            "MinecraftComesAlive.xml",
            "MinecraftSpecialRules.xml",
            "ModernWarfare.xml",
            "NetherOres.xml",
            "Netherrocks.xml",
            "Nuclearcraft.xml",
            "PamsHarvestCraft.xml",
            "ProjectRed.xml",
            "RailCraft.xml",
            "ReactorCraft.xml",
            "SimpleOres.xml",
            "Thaumcraft4.xml",
            "ThermalFoundation.xml",
            "TinkersConstruct.xml",
            "TinkersSteelworks.xml",
            "Vanilla.xml"
        };
        for (String module : extraModules) {
        	unpackConfigFile("modules/" + module, new File(defaultModulesDir, module));
        }
        
        new File(modulesDir, "custom").mkdir();

        unpackConfigFile(DEFAULT_BASE_CONFIG_FILENAME, new File(configPath, DEFAULT_BASE_CONFIG_FILENAME));
        loadWorldConfig();
	}

	private static WorldConfig loadWorldConfig() {
    	WorldConfig config = null;

        while (config == null)
        {
            try
            {
                config = new WorldConfig();
            }
            catch (Exception e)
            {
                if (!ServerState.onConfigError(e))
                {
                    break;
                }

                config = null;
            }
        }
        
        return config;
	}
	
    public static boolean unpackConfigFile(String configName, File destination)
    {
    	String resourceName = "config/" + configName;
        try
            {
        	    InputStream ex = CustomOreGenBase.class.getClassLoader().getResourceAsStream(resourceName);
                BufferedOutputStream streamOut = new BufferedOutputStream(new FileOutputStream(destination));
                byte[] buffer = new byte[1024];
                int len1;

                while ((len1 = ex.read(buffer)) >= 0)
                {
                    streamOut.write(buffer, 0, len1);
                }

                ex.close();
                streamOut.close();
                return true;
            }
            catch (Exception var6)
            {
                throw new RuntimeException("Failed to unpack resource \'" + resourceName + "\'", var6);
            }
    }

    public static File getConfigDir() {
    	return FMLPaths.CONFIGDIR.get().resolve(MODID).toFile();
    }
    
    public static boolean hasMystcraft()
    {
        if (_hasMystcraft == 0)
        {
            try
            {
                _hasMystcraft = -1;

                if (ModList.get().isLoaded("Mystcraft"))
                {
                	/* FIXME: re-enable after restoring Mystcraft compatibility
                    MystcraftInterface.init();
                    _hasMystcraft = 1;
                    */
                }
            }
            catch (Throwable var1)
            {
                log.error("COG Mystcraft interface appears to be incompatible with the installed version of Mystcraft.", var1);
            }
        }

        return _hasMystcraft == 1;
    }
    
	private static ModContainer getModContainer() {
		return ModList.get().getModContainerByObject(instance).get();
	}

	public static String getDisplayString() {
		ModContainer metadata = getModContainer();
    	return metadata.getModId() + " " + metadata.getModInfo().getVersion();
	}
}


<|start_filename|>src/main/java/CustomOreGen/Server/GuiCustomOreGenSettings.java<|end_filename|>
package CustomOreGen.Server;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;


import com.mojang.blaze3d.platform.GlStateManager;

import CustomOreGen.Server.ConfigOption.DisplayGroup;
import CustomOreGen.Util.Localization;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.IGuiEventListener;
import net.minecraft.client.gui.SlotGui;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.AbstractSlider;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.gui.widget.button.AbstractButton;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

@OnlyIn(Dist.CLIENT)
public class GuiCustomOreGenSettings extends Screen
{
	protected final Screen _parentGui;
	protected int refreshGui = 2;
	protected Button _doneButton = null;
	protected Button _resetButton = null;
	protected GuiCustomOreGenSettings.GuiOptionSlot _optionPanel = null;
	protected GuiGroupPanel _groupPanel = null;
	protected String _toolTip = null;

	public GuiCustomOreGenSettings(Screen parentGui)
	{
		super(new StringTextComponent("Custom Ore Gen"));
		this._parentGui = parentGui;
	}

	@Override
	public void init()
	{
		super.init();
		super.buttons.clear();
		ConfigOption.DisplayGroup currentGroup = this._groupPanel == null ? null : this._groupPanel.getSelectedGroup();

		if (this.refreshGui >= 2)
		{
			if (this.refreshGui >= 3)
			{
				WorldConfig.loadedOptionOverrides[0] = null;
			}

			WorldConfig visibleGroups = null;

			while (visibleGroups == null)
			{
				try
				{
					visibleGroups = new WorldConfig();
				}
				catch (Exception var7)
				{
					if (ServerState.onConfigError(var7))
					{
						visibleGroups = null;
						continue;
					}

					visibleGroups = WorldConfig.createEmptyConfig();
				}

				WorldConfig.loadedOptionOverrides[0] = new ArrayList<ConfigOption>();
				for (ConfigOption option : visibleGroups.getConfigOptions()) {
					if (option.getDisplayState() != null && option.getDisplayState() != ConfigOption.DisplayState.hidden)
					{
						WorldConfig.loadedOptionOverrides[0].add(option);
					}
				}

				if (currentGroup != null)
				{
					ConfigOption visibleOptions = visibleGroups.getConfigOption(currentGroup.getName());

					if (visibleOptions instanceof ConfigOption.DisplayGroup)
					{
						currentGroup = (ConfigOption.DisplayGroup)visibleOptions;
					}
				}
			}
		}

		this.refreshGui = 0;
		Vector<ConfigOption.DisplayGroup> visibleGroups1 = new Vector<DisplayGroup>();
		Vector<ConfigOption> visibleOptions1 = new Vector<ConfigOption>();

		nextOption: for (ConfigOption option : WorldConfig.loadedOptionOverrides[0]) {

			ConfigOption.DisplayGroup group;

			if (option instanceof ConfigOption.DisplayGroup)
			{
				for (group = currentGroup; group != option.getDisplayGroup(); group = group.getDisplayGroup())
				{
					if (group == null)
					{
						continue nextOption;
					}
				}

				visibleGroups1.add((ConfigOption.DisplayGroup)option);
			}
			else
			{
				for (group = option.getDisplayGroup(); group != currentGroup; group = group.getDisplayGroup())
				{
					if (group == null)
					{
						continue nextOption;
					}
				}

				visibleOptions1.add(option);
			}
		}

		this._groupPanel = new GuiGroupPanel(0, 20, super.width, 20, currentGroup, visibleGroups1, this);
		this._optionPanel = new GuiOptionSlot(Minecraft.getInstance(), visibleGroups1.isEmpty() ? 16 : 40, super.height - 30, 25, visibleOptions1, this);
		//TODO: fix buttons
		//this._optionPanel.registerScrollButtons(1, 2);
		if(_doneButton == null) {
			this._doneButton = this.addButton(new Button(super.width / 2 - 155, super.height - 24, 150, 20, "Done", this::actionPerformed));
			this._resetButton = this.addButton(new Button(super.width / 2 + 5, super.height - 24, 150, 20, "Defaults", this::actionPerformed));
		}
		else {
			this.addButton(_doneButton);
			this.addButton(_resetButton);
		}
	}

	@Override
	public boolean mouseClicked(double p_mouseClicked_1_, double p_mouseClicked_3_, int p_mouseClicked_5_) {
		for(IGuiEventListener iguieventlistener : this.children()) {
			if (iguieventlistener.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_)) {
				this.setFocused(iguieventlistener);
				if (p_mouseClicked_5_ == 0) {
					this.setDragging(true);
				}

				return true;
			}
		}
		this._optionPanel.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_);
		this._groupPanel.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_);
		for(IOptionControl control : _optionPanel._optionControls) {
			Widget wid = control.getControl();
			if(wid.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_)) {
				this.setFocused(wid);
				if (p_mouseClicked_5_ == 0) {
					this.setDragging(true);
				}

				return true;
			}
		}
		/*if(_groupPanel.isInScrollArea((int)p_mouseClicked_1_, (int)p_mouseClicked_3_)) {
			int j = _groupPanel.getScrollPos();
			if(j > 0) {
				
			}
		}*/
		return false;
	}
	
	

	@Override
	public boolean mouseDragged(double p_mouseDragged_1_, double p_mouseDragged_3_, int p_mouseDragged_5_, double p_mouseDragged_6_, double p_mouseDragged_8_) {
		_optionPanel.mouseDragged(p_mouseDragged_1_, p_mouseDragged_3_, p_mouseDragged_5_, p_mouseDragged_6_, p_mouseDragged_8_);
		return super.mouseDragged(p_mouseDragged_1_, p_mouseDragged_3_, p_mouseDragged_5_, p_mouseDragged_6_, p_mouseDragged_8_);
	}

	@Override
	public boolean mouseScrolled(double p_mouseScrolled_1_, double p_mouseScrolled_3_, double p_mouseScrolled_5_) {
		_optionPanel.mouseScrolled(p_mouseScrolled_1_, p_mouseScrolled_3_, p_mouseScrolled_5_);
		return super.mouseScrolled(p_mouseScrolled_1_, p_mouseScrolled_3_, p_mouseScrolled_5_);
	}

	protected void actionPerformed(Button button)
	{
		//super.actionPerformed(button);
		if (button == this._doneButton)
		{
			super.minecraft.displayGuiScreen(this._parentGui);
		}
		else if (button == this._resetButton)
		{
			this.refreshGui = 3;
		}
		else
		{
			//TODO
			//this._optionPanel.actionPerformed(button);
		}
	}

	/*@Override
    public void handleMouseInput() throws IOException {
    	super.handleMouseInput();
    	this._optionPanel.handleMouseInput();
    }*/

	/*@Override
    protected void mouseClicked(int mouseX, int mouseY, int buttonID) throws IOException
    {
        super.mouseClicked(mouseX, mouseY, buttonID);
        this._groupPanel.mouseClicked(mouseX, mouseY, buttonID);
        this._optionPanel.handleMouseInput();
    }*/

	@Override
	public void render(int mouseX, int mouseY, float partialTick)
	{
		this.renderBackground();
		this._optionPanel.render(mouseX, mouseY, partialTick);
		this._groupPanel.render(mouseX, mouseY, partialTick);
		this.drawCenteredString(super.font, "CustomOreGen Options", super.width / 2, 4, 16777215);
		super.render(mouseX, mouseY, partialTick);

		if (this._toolTip != null)
		{
			List<String> lines = super.font.listFormattedStringToWidth(this._toolTip, 2 * super.width / 5 - 8);
			int[] lineWidths = new int[lines.size()];
			int tipW = 0;
			int tipH = 8 + super.font.FONT_HEIGHT * lines.size();
			int l = 0;

			for (Iterator<String> tipX = lines.iterator(); tipX.hasNext(); ++l)
			{
				String tipY = tipX.next();
				lineWidths[l] = super.font.getStringWidth(tipY) + 8;

				if (tipW < lineWidths[l])
				{
					tipW = lineWidths[l];
				}
			}

			int tipX = mouseX;
			int tipY = mouseY;

			if (mouseX > 2 * super.width / 5)
			{
				tipX = mouseX - tipW;
			}

			if (mouseY > super.height / 2)
			{
				tipY = mouseY - tipH;
			}

			this.fillGradient(tipX, tipY, tipX + tipW, tipY + tipH, -15724528, -14671840);
			l = 0;

			for (String line : lines) {
				super.font.drawString(line, tipX + (tipW - lineWidths[l]) / 2 + 4, tipY + 4 + l * super.font.FONT_HEIGHT, 16777215);
				++l;
			}

			this._toolTip = null;
		}

		if (this.refreshGui > 0)
		{
			this.init();
		}
	}

	public class GuiGroupPanel
	{
		protected final Screen _parentGui;
		protected int posX;
		protected int posY;
		protected int width;
		protected int height;
		protected int _scrollHInset;
		protected int _scrollOffsetX;
		protected int _scrollOffsetMax;
		protected int mouseX;
		protected int mouseY;
		protected final Vector<IOptionControl> _groupButtons;
		protected Button _groupScrollLButton;
		protected Button _groupScrollRButton;
		protected IOptionControl _currentGroup;
		protected Button _currentButton;

		public GuiGroupPanel(int x, int y, int width, int height, ConfigOption.DisplayGroup selGroup, Vector<ConfigOption.DisplayGroup> groups, Screen parentGui)
		{
			this._parentGui = parentGui;
			this.posX = 0;
			this.posY = 0;
			this.width = 0;
			this.height = 0;
			this._scrollHInset = 0;
			this._scrollOffsetX = 0;
			this._scrollOffsetMax = 0;
			this.mouseX = 0;
			this.mouseY = 0;
			this._groupButtons = new Vector<IOptionControl>();
			this._groupScrollLButton = null;
			this._groupScrollRButton = null;
			this._currentGroup = null;
			this._currentButton = null;
			this.posX = x;
			this.posY = y;
			this.width = width;
			this.height = height;
			int scrollWidth = 0;
			int curSelX = 0;

			for (int c = -1; !groups.isEmpty() && c < groups.size(); ++c)
			{
				ConfigOption.DisplayGroup group = c < 0 ? null : groups.get(c);
				String ALL = Localization.maybeLocalize("ALL.displayName", "ALL");
				String text = c < 0 ? "[ " + ALL + " ]" : group.getLocalizedDisplayName();
				int btnWidth = font.getStringWidth(text) + 10;
				GuiGroupButton control = new GuiGroupButton(scrollWidth, 0, btnWidth, height, text, group);
				this._groupButtons.add(control);

				if (group == selGroup)
				{
					curSelX = (width - btnWidth) / 2 - scrollWidth;
					this._currentGroup = control;
				}

				scrollWidth += btnWidth;
			}

			this._scrollHInset = 4;
			this._scrollOffsetMax = width - 2 * this._scrollHInset - scrollWidth;

			if (this._scrollOffsetMax < 0)
			{
				this._groupScrollLButton = new Button(x + 4, y, 20, height, "<", this::onPressed);
				this._groupScrollRButton = new Button(x + width - 24, y, 20, height, ">", this::onPressed);
				this._scrollHInset = 26;
				this._scrollOffsetMax = width - 2 * this._scrollHInset - scrollWidth;
				this._scrollOffsetX = Math.min(Math.max(curSelX - this._scrollHInset, this._scrollOffsetMax), 0);
			}
			else
			{
				this._scrollOffsetX = this._scrollOffsetMax / 2;
			}
		}

		public void onPressed(Button btn) {
			if (this._groupScrollLButton == btn)
			{
				this._scrollOffsetX = Math.min(0, this._scrollOffsetX + 1);
			}
			if (this._groupScrollRButton == btn)
			{
				this._scrollOffsetX = Math.max(this._scrollOffsetMax, this._scrollOffsetX - 1);
			}
		}

		public ConfigOption.DisplayGroup getSelectedGroup()
		{
			return this._currentGroup == null ? null : (ConfigOption.DisplayGroup)this._currentGroup.getOption();
		}

		public int getScrollPos()
		{
			return this._scrollOffsetX;
		}

		public boolean isInScrollArea(int x, int y)
		{
			int rx = x - this.posX;
			int ry = y - this.posY;
			return rx >= this._scrollHInset && rx <= this.width - this._scrollHInset && ry > 0 && ry < this.height;
		}

		public void render(int mouseX, int mouseY, float partialTick)
		{
			this.mouseX = mouseX;
			this.mouseY = mouseY;
			Iterator<IOptionControl> tess = this._groupButtons.iterator();

			while (tess.hasNext())
			{
				IOptionControl texSize = tess.next();
				texSize.getControl().render(mouseX, mouseY, partialTick);
			}

			double texSize1 = 32.0D;
			GlStateManager.disableDepthTest();
			Tessellator tessellator = Tessellator.getInstance();
			BufferBuilder vertexbuffer = tessellator.getBuffer();
			minecraft.getTextureManager().bindTexture(BACKGROUND_LOCATION);
			GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
			vertexbuffer.begin(7, DefaultVertexFormats.POSITION_TEX_COLOR);
			vertexbuffer.pos((double)this.posX, (double)(this.posY + this.height), 0.0D).tex(0.0D, (double)this.height / texSize1);
			vertexbuffer.pos((double)(this.posX + this._scrollHInset), (double)(this.posY + this.height), 0.0D).tex((double)this._scrollHInset / texSize1, (double)this.height / texSize1);
			vertexbuffer.pos((double)(this.posX + this._scrollHInset), (double)this.posY, 0.0D).tex((double)this._scrollHInset / texSize1, 0.0D);
			vertexbuffer.pos((double)this.posX, (double)this.posY, 0.0D).tex(0.0D, 0.0D);
			vertexbuffer.pos((double)(this.posX + this.width - this._scrollHInset), (double)(this.posY + this.height), 0.0D).tex(0.0D, (double)this.height / texSize1);
			vertexbuffer.pos((double)(this.posX + this.width), (double)(this.posY + this.height), 0.0D).tex((double)this._scrollHInset / texSize1, (double)this.height / texSize1);
			vertexbuffer.pos((double)(this.posX + this.width), (double)this.posY, 0.0D).tex((double)this._scrollHInset / texSize1, 0.0D);
			vertexbuffer.pos((double)(this.posX + this.width - this._scrollHInset), (double)this.posY, 0.0D).tex(0.0D, 0.0D);
			tessellator.draw();
		}

		public void mouseClicked(double p_mouseClicked_1_, double p_mouseClicked_3_, int p_mouseClicked_5_)
        {
            if (p_mouseClicked_5_ == 0)
            {
                this._currentButton = null;

                for(IOptionControl control : this._groupButtons) {
        			Widget wid = control.getControl();
        			if(wid.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_)) {
        				this._parentGui.setFocused(wid);
        				if (p_mouseClicked_5_ == 0) {
        					this._parentGui.setDragging(true);
        				}
        				break;
        			}
        		}

                if (this._groupScrollLButton != null && this._groupScrollLButton.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_))
                {
                    this._currentButton = this._groupScrollLButton;
                }
                else if (this._groupScrollRButton != null && this._groupScrollRButton.mouseClicked(p_mouseClicked_1_, p_mouseClicked_3_, p_mouseClicked_5_))
                {
                    this._currentButton = this._groupScrollRButton;
                }
            }
            
        }

		class GuiGroupButton extends AbstractButton implements IOptionControl
		{
			protected final ConfigOption.DisplayGroup _group;
			private final int _relX;
			private final int _relY;

			public GuiGroupButton(int relX, int relY, int width, int height, String text, ConfigOption.DisplayGroup group)
			{
				super(relX, relY, width, height, text);
				this._group = group;
				this._relX = relX;
				this._relY = relY;
			}

			public ConfigOption getOption()
			{
				return this._group;
			}

			public Widget getControl()
			{
				return this;
			}

			private boolean isButtonVisible()
			{
				super.x = posX + _scrollHInset + _scrollOffsetX + this._relX;
				super.y = posY + this._relY;
				return !isInScrollArea(super.x, super.y) && !isInScrollArea(super.x + super.width, super.y + super.height);
			}

			@Override
			public void onPress()
			{
				if (this.isButtonVisible())
				{
					_currentGroup = this;
					refreshGui = 1;
				}
			}

			/*@Override
            public int getHoverState(boolean mouseOver)
            {
                if (isInScrollArea(mouseX, mouseY))
                {
                    return super.getHoverState(mouseOver);
                }
                else
                {
                    super.hovered = false;
                    return 1;
                }
            }*/

			@Override
			public void render(int mouseX, int mouseY, float partialTick)
			{
				if (this.isButtonVisible() && super.visible)
				{
					super.isHovered = mouseX >= super.x && mouseY >= super.y && mouseX < super.x + super.width && mouseY < super.y + super.height;
					//TODO: mouse drag?
					//this.mouseDragged(mc, mouseX, mouseY);

					if (_currentGroup == this)
					{
						fill(super.x, super.y, super.x + super.width, super.y + super.height, -1610612736);
					}
					else
					{
						if (this._group == null)
						{
							fill(super.x, super.y, super.x + 1, super.y + super.height, -16777216);
						}

						fill(super.x, super.y, super.x + super.width, super.y + 1, -16777216);
						fill(super.x + super.width - 1, super.y, super.x + super.width, super.y + super.height, -16777216);
					}

					FontRenderer fontObj = Minecraft.getInstance().fontRenderer;
					int textColor = super.active ? (super.isHovered ? 16777120 : 14737632) : 10526880;
					this.drawCenteredString(fontObj, super.getMessage(), super.x + super.width / 2, super.y + (super.height - 8) / 2, textColor);

					if (this.isMouseOver(mouseX, mouseY) && this._group != null)
					{
						_toolTip = this._group.getLocalizedDescription();
					}
				}
			}
		}

	}

	public class GuiOptionSlot extends SlotGui
	{
		protected final Screen _parentGui;
		protected final Vector<IOptionControl> _optionControls;
		protected IOptionControl _clickTarget;

		public GuiOptionSlot(Minecraft minecraft, int top, int bottom, int slotHeight, Vector<ConfigOption> options, Screen parentGui)
		{
			super(minecraft, GuiCustomOreGenSettings.this.width, GuiCustomOreGenSettings.this.height, top, bottom, slotHeight);
			this._parentGui = parentGui;
			this._optionControls = new Vector<IOptionControl>();
			this._clickTarget = null;

			for (int c = 0; c < options.size(); ++c)
			{
				ConfigOption option = options.get(c);

				if (option instanceof ChoiceOption)
				{
					this._optionControls.add(new GuiChoiceButton(c, 2 * width / 5 + 15, 0, width / 10 + 100, slotHeight - 6, (ChoiceOption)option));
				}
				else if (option instanceof NumericOption)
				{
					this._optionControls.add(new GuiNumericSlider(c, 2 * width / 5 + 15, 0, width / 10 + 100, slotHeight - 6, (NumericOption)option));
				}
			}
		}

		@Override
		protected int getItemCount()
		{
			return this._optionControls.size();
		}

		/*@Override
        protected void elementClicked(int index, boolean doubleClicked, int mouseX, int mouseY)
        {
            if (this._clickTarget != null)
            {
                this._clickTarget.getControl().mouseReleased(mouseX, mouseY);

                if (this._clickTarget.getOption().getDisplayState() == ConfigOption.DisplayState.shown_dynamic)
                {
                    refreshGui = 2;
                }

                this._clickTarget = null;
            }

            IOptionControl control = (IOptionControl)this._optionControls.get(index);

            if (control.getControl().mousePressed(mc, mouseX, mouseY))
            {
            	Minecraft.getInstance().getSoundHandler().play(SimpleSound.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));
                this._clickTarget = control;
            }
        }*/

		@Override
		protected boolean isSelectedItem(int index)
		{
			return false;
		}

		@Override
		protected void renderBackground() {

		}

		@Override
		protected void renderItem(int index, int slotX, int slotY, int slotH, int mouseX, int mouseY, float partialTick)
		{
			IOptionControl optCtrl = (IOptionControl)this._optionControls.get(index);
			ConfigOption option = optCtrl.getOption();
			Widget control = optCtrl.getControl();
			String optionName = option.getLocalizedDisplayName();
			int nameW = font.getStringWidth(optionName);
			int nameX = 2 * width / 5 - 15 - nameW;
			int nameH = font.FONT_HEIGHT;
			int nameY = slotY + 8;
			drawString(font, optionName, nameX, nameY, 16777215);

			if (mouseX >= nameX && mouseX <= nameX + nameW && mouseY >= nameY && mouseY <= nameY + nameH && this.isMouseInList(mouseX, mouseY))
			{
				_toolTip = option.getLocalizedDescription();
			}
			//TODO: fix this
			//if(control == null) return;
			control.y = slotY + 3;
			control.render(mouseX, mouseY, partialTick);
		}

		//TODO: is this needed?
		/*@Override
        public void render(int mouseX, int mouseY, float partialTick)
        {
            super.render(mouseX, mouseY, partialTick);

            if (this._clickTarget != null && !Mouse.isButtonDown(0))
            {
                this._clickTarget.getControl().mouseReleased(mouseX, mouseY);

                if (this._clickTarget.getOption().getDisplayState() == ConfigOption.DisplayState.shown_dynamic)
                {
                    refreshGui = 2;
                }

                this._clickTarget = null;
            }
        }*/

		class GuiChoiceButton extends AbstractButton implements IOptionControl
		{
			private final ChoiceOption _choice;
			private final int _maxWidth;

			public GuiChoiceButton(int id, int x, int y, int maxWidth, int height, ChoiceOption choice)
			{
				super(x, y, maxWidth, height, (String)null);
				this._choice = choice;
				this._maxWidth = maxWidth;
				this.onValueChanged();
			}

			public ConfigOption getOption()
			{
				return this._choice;
			}

			public Widget getControl()
			{
				return this;
			}

			protected void onValueChanged()
			{
				super.setMessage(this._choice.getLocalizedDisplayValue());
				int strWidth = super.getMessage() == null ? 0 : font.getStringWidth(super.getMessage());
				super.width = Math.min(this._maxWidth, strWidth + 10);
			}

			@Override
			public void onPress()
			{
				this._choice.setValue(this._choice.nextPossibleValue());
				this.onValueChanged();
			}

			/*@Override
            public int getHoverState(boolean mouseOver)
            {
                if (GuiOptionSlot.this.isInBounds(this.mouseX, this.mouseY))
                {
                    return super.getHoverState(mouseOver);
                }
                else
                {
                    super.hovered = false;
                    return 1;
                }
            }*/

			@Override
			public void render(int mouseX, int mouseY, float partialTick)
			{
				super.render(mouseX, mouseY, partialTick);

				if (this.isMouseOver(mouseX, mouseY))
				{
					_toolTip = this._choice.getLocalizedValueDescription();
				}
			}
		}

		class GuiNumericSlider extends AbstractSlider implements IOptionControl
		{
			private final NumericOption _numeric;

			public GuiNumericSlider(int id, int x, int y, int width, int height, NumericOption numeric)
			{
				super(x, y, width, height, 0);            
				this._numeric = numeric;
				super.width = width;
				super.height = height;
				//ReflectionHelper.setPrivateValue(GuiOptionSlider.class, this, (float)numeric.getNormalizedDisplayValue(), 0);
				this.value = .2d;//(double) _numeric.getValue();
				this.applyValue();
			}

			public ConfigOption getOption()
			{
				return this._numeric;
			}

			@Override
			public Widget getControl() {
				return this;
			}

			@Override
			protected void updateMessage() {

			}

			@Override
			protected void applyValue() {
				this._numeric.setNormalizedDisplayValue(this.value);
				long prec = 6L;
				long base = (long)Math.pow(10.0D, (double)prec);

				for (long val = Math.round(this._numeric.getDisplayIncr() * (double)base); prec > 0L && val % 10L == 0L; --prec)
				{
					val /= 10L;
					base /= 10L;
				}

				super.setMessage(String.format("%." + prec + "f", new Object[] {Double.valueOf(this._numeric.getDisplayValue())}));
			}

			/*private void updateSliderValue(int mouseX) {
            	double sliderValue = (float)(mouseX - (this.x + 4)) / (float)(this.width - 8);

                if (sliderValue < 0.0F)
                {
                    sliderValue = 0.0F;
                }

                if (sliderValue > 1.0F)
                {
                    sliderValue = 1.0F;
                }

                this._numeric.setNormalizedDisplayValue(sliderValue);
            	this.applyValue();
            }*/

			/*@Override
            public boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY)
            {
                if (super.mousePressed(minecraft, mouseX, mouseY))
                {
                	this.updateSliderValue(mouseX);
                    return true;
                }
                else
                {
                    return false;
                }
            }*/

			/*@Override
            protected void mouseDragged(Minecraft minecraft, int mouseX, int mouseY)
            {
                super.mouseDragged(minecraft, mouseX, mouseY);
                if (super.dragging) {
                	this.updateSliderValue(mouseX);
                }
            }*/
		}
	}

	public interface IOptionControl
	{
		ConfigOption getOption();

		Widget getControl();
	}

}


<|start_filename|>src/main/java/CustomOreGen/Config/ValidatorBiomeDescriptor.java<|end_filename|>
package CustomOreGen.Config;

import org.w3c.dom.Node;

import CustomOreGen.Util.BiomeDescriptor.BiomeRestriction;

public class ValidatorBiomeDescriptor extends ValidatorNode
{
    public String biome = null;
    public float weight = 1.0F;
    public BiomeRestriction restriction = new BiomeRestriction(); 

    protected ValidatorBiomeDescriptor(ValidatorNode parent, Node node)
    {
        super(parent, node);
    }

    protected boolean validateChildren() throws ParserException
    {
        super.validateChildren();
        this.biome = this.validateRequiredAttribute(String.class, "Name", true);
        this.weight = this.validateNamedAttribute(Float.class, "Weight", this.weight, true);
        float minTemperature = this.validateNamedAttribute(Float.class, "MinTemperature", restriction.minTemperature, true);
        float maxTemperature = this.validateNamedAttribute(Float.class, "MaxTemperature", restriction.maxTemperature, true);
        float minRainfall = this.validateNamedAttribute(Float.class, "MinRainfall", restriction.minRainfall, true);
        float maxRainfall = this.validateNamedAttribute(Float.class, "MaxRainfall", restriction.maxRainfall, true);
        float minDepth = this.validateNamedAttribute(Float.class, "MinDepth", restriction.minDepth, true);
        float maxDepth = this.validateNamedAttribute(Float.class, "MaxDepth", restriction.maxDepth, true);
        float minScale = this.validateNamedAttribute(Float.class, "MinScale", restriction.minScale, true);
        float maxScale = this.validateNamedAttribute(Float.class, "MaxScale", restriction.maxScale, true);
        this.restriction = new BiomeRestriction(
        		minTemperature, maxTemperature, 
        		minRainfall, maxRainfall,
        		minDepth, maxDepth,
        		minScale, maxScale);
        return true;
    }
    
    public static class Factory implements IValidatorFactory<ValidatorBiomeDescriptor>
    {
        public ValidatorBiomeDescriptor createValidator(ValidatorNode parent, Node node)
        {
            return new ValidatorBiomeDescriptor(parent, node);
        }
    }

}


<|start_filename|>src/main/java/CustomOreGen/ForgeEventBusSubscriber.java<|end_filename|>
package CustomOreGen;

import java.util.Iterator;

import com.mojang.brigadier.CommandDispatcher;

import CustomOreGen.Client.ClientState;
import CustomOreGen.Server.ConsoleCommands;
import CustomOreGen.Server.ServerState;
import CustomOreGen.Server.WorldGenFeature;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.CreateWorldScreen;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.command.CommandSource;
import net.minecraft.tags.Tag;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.GenerationStage.Decoration;
import net.minecraft.world.gen.feature.BlockBlobConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.feature.OreFeatureConfig;
import net.minecraft.world.gen.feature.ReplaceBlockConfig;
import net.minecraft.world.gen.feature.SphereReplaceConfig;
import net.minecraft.world.gen.placement.IPlacementConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.WorldInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.Tags;
import net.minecraftforge.event.TagsUpdatedEvent;
import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.event.TickEvent.Phase;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.registries.ForgeRegistries;

@EventBusSubscriber(modid = CustomOreGenBase.MODID)
public class ForgeEventBusSubscriber
{
	
	@SubscribeEvent
    public static void onTagsUpdated(TagsUpdatedEvent event)
    {
    	for(Biome biome : ForgeRegistries.BIOMES) {
        	removeAllOreGenerators(Tags.Blocks.ORES, biome);
        	//UNDERGROUND_DECORATION happens after UNDERGROUND_ORES
        	//Uses Placement.NOPE IPlacementConfig.NO_PLACEMENT_CONFIG as the feature will handle itself
    		biome.addFeature(GenerationStage.Decoration.UNDERGROUND_DECORATION, Biome.createDecoratedFeature(WorldGenFeature.FEATURE, IFeatureConfig.NO_FEATURE_CONFIG, Placement.NOPE, IPlacementConfig.NO_PLACEMENT_CONFIG));
    	}
    }
    
    private static void removeAllOreGenerators(Tag<Block> tag, Biome biome) {
		Iterator<ConfiguredFeature<?>> it = biome.getFeatures(Decoration.UNDERGROUND_ORES).iterator();
		while(it.hasNext()) { //for each feature in that biome
			ConfiguredFeature<?> feature = it.next();
			if(feature.config instanceof DecoratedFeatureConfig) {
				DecoratedFeatureConfig dfconfig = (DecoratedFeatureConfig)feature.config;
				if(dfconfig.feature.config instanceof OreFeatureConfig) { //if it is an ore gen feature
					OreFeatureConfig oreConfigl = (OreFeatureConfig)dfconfig.feature.config;
					if(oreConfigl.state.isIn(tag)) {
						it.remove(); //remove it
					}
				}
				if(dfconfig.feature.config instanceof ReplaceBlockConfig) { //emerald ore uses this
					ReplaceBlockConfig oreConfigl = (ReplaceBlockConfig)dfconfig.feature.config;
					if(oreConfigl.state.isIn(tag)) {
						it.remove(); //remove it
					}
				}
				if(dfconfig.feature.config instanceof BlockBlobConfig) { //emerald ore uses this
					BlockBlobConfig oreConfigl = (BlockBlobConfig)dfconfig.feature.config;
					if(oreConfigl.state.isIn(tag)) {
						it.remove(); //remove it
					}
				}
				if(dfconfig.feature.config instanceof SphereReplaceConfig) { //emerald ore uses this
					SphereReplaceConfig oreConfigl = (SphereReplaceConfig)dfconfig.feature.config;
					if(oreConfigl.state.isIn(tag)) {
						it.remove(); //remove it
					}
				}
				//there are other implementors of IFeatureConfig
				//such as clay deposits (BlockWithContextConfig) that may also be of interest
			}
		}
	}
    
    @SubscribeEvent
    public static void onServerStarting(FMLServerStartingEvent event)
    {
        ServerState.checkIfServerChanged(event.getServer(), (WorldInfo)null);    
        registerCommands(event.getCommandDispatcher());
    }

    private static void registerCommands(CommandDispatcher<CommandSource> dispatcher) {
    	CustomOreGenBase.log.debug("Registering Console command interface ...");
        ConsoleCommands.register(dispatcher);
	}

    @OnlyIn(Dist.CLIENT)
    @SubscribeEvent
    public static void onClientTick(ClientTickEvent event)
    {
    	if (event.phase != Phase.END) {
    		return;
    	}
        Minecraft mc = Minecraft.getInstance();

        if (mc.world != null && ClientState.hasWorldChanged(mc.world))
        {
            ClientState.onWorldChanged(mc.world);
        }
    }

    @SubscribeEvent
    public static void onClientLogin(PlayerLoggedInEvent event)
    {
        World handlerWorld = event.getPlayer().world;
        ServerState.checkIfServerChanged(handlerWorld.getServer(), 
        		handlerWorld.getWorldInfo());
    }
    
    @SubscribeEvent
    @OnlyIn(Dist.CLIENT)
    public static void onRenderWorldLast(RenderWorldLastEvent event)
    {
        ClientState.onRenderWorld(Minecraft.getInstance().getRenderViewEntity(), (double)event.getPartialTicks());
    }

    @SubscribeEvent
    public static void onLoadWorld(WorldEvent.Load event)
    {
    	World world = event.getWorld().getWorld();
        if (world instanceof ServerWorld)
        {
            ServerState.checkIfServerChanged(world.getServer(), world.getWorldInfo());
            ServerState.getWorldConfig(world);
        }
        else if (event.getWorld() instanceof ClientWorld && ClientState.hasWorldChanged(world))
        {
            ClientState.onWorldChanged(world);
        }
    }

    @SubscribeEvent
    public static void onLoadChunk(ChunkEvent.Load event)
    {
    	// TODO: call populateDistributions, but instruct it to only generate if there is a version, and it's old
    }
    
    @SubscribeEvent
    @OnlyIn(Dist.CLIENT)
    public static void onInitGui(GuiScreenEvent.InitGuiEvent.Post event) {
        if (event.getGui() instanceof CreateWorldScreen)
        {
            ServerState.addOptionsButtonToGui((CreateWorldScreen)event.getGui(), event.getWidgetList(), event::addWidget);
        }
    }
    
    @SubscribeEvent
    @OnlyIn(Dist.CLIENT)
    public static void onActionPerformed(GuiScreenEvent.ActionPerformedEvent.Post event) {
        if (event.getGui() instanceof CreateWorldScreen)
        {
            ServerState.updateOptionsButtonVisibility((CreateWorldScreen)event.getGui());
        }
    }
}


<|start_filename|>src/main/java/CustomOreGen/Server/ServerState.java<|end_filename|>
package CustomOreGen.Server;

import java.awt.Frame;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Consumer;

import CustomOreGen.CustomOreGenBase;
import CustomOreGen.GeometryData;
import CustomOreGen.GeometryRequestData;
import CustomOreGen.Util.GeometryStream;
import CustomOreGen.Util.SimpleProfiler;
import net.minecraft.client.gui.screen.CreateWorldScreen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.World;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.storage.WorldInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;

public class ServerState
{
    private static MinecraftServer _server = null;
    private static Map<World,WorldConfig> _worldConfigs = new HashMap<World, WorldConfig>();
    private static Button _optionsGuiButton = null;
    private static boolean forcingChunk;

    public static WorldConfig getWorldConfig(World world)
    {
        WorldConfig cfg = _worldConfigs.get(world);

        while (cfg == null)
        {
            try
            {
                cfg = new WorldConfig(world);
                validateOptions(cfg.getConfigOptions(), true);
                validateDistributions(cfg.getOreDistributions(), true);
            }
            catch (Exception var4)
            {
                if (onConfigError(var4))
                {
                    cfg = null;
                    continue;
                }

                cfg = WorldConfig.createEmptyConfig();
            }

            _worldConfigs.put(world, cfg);
        }

        return cfg;
    }

    public static void clearWorldConfig(World world)
    {
        _worldConfigs.remove(world);
    }

    public static boolean onConfigError(Throwable error)
    {
        CustomOreGenBase.log.error("Problem loading world config", error);
        Frame[] frames = Frame.getFrames();

        if (frames != null && frames.length > 0)
        {
            switch ((new ConfigErrorDialog()).showDialog(frames[0], error))
            {
                case 1:
                    return true;

                case 2:
                    return false;
            }
        }

        return false;
    }

    public static void validateDistributions(Collection<IOreDistribution> distributions, boolean cull) throws IllegalStateException
    {
        Iterator<IOreDistribution> it = distributions.iterator();

        while (it.hasNext())
        {
            IOreDistribution dist = it.next();

            if (!dist.validate() && cull)
            {
                it.remove();
            }
        }
    }

    public static void validateOptions(Collection<ConfigOption> options, boolean cull)
    {
        Iterator<ConfigOption> it = options.iterator();

        while (it.hasNext())
        {
            ConfigOption option = (ConfigOption)it.next();

            if (cull && option instanceof ConfigOption.DisplayGroup)
            {
                it.remove();
            }
        }
    }

    // TODO: add force option; if true then we will generate even when there is no version
    public static void populateDistributions(Collection<IOreDistribution> distributions, World world, int chunkX, int chunkZ)
    {
        SimpleProfiler.globalProfiler.startSection("Populate");
        
        // FIXME: Before 1.14, we were forcing instant block updates here. Not clear how to do that now.
        
        for (IOreDistribution dist : distributions) {
        	dist.generate(world, chunkX, chunkZ);
            dist.populate(world, chunkX, chunkZ);
            dist.cull();
        }
        
        SimpleProfiler.globalProfiler.endSection();
    }

    public static GeometryData getDebuggingGeometryData(GeometryRequestData request)
    {
        if (_server == null)
        {
            return null;
        }
        else if (request.world == null)
        {
            return null;
        }
        else
        {
            WorldConfig cfg = getWorldConfig(request.world);

            if (!cfg.debuggingMode)
            {
                return null;
            }
            else
            {
                LinkedList<GeometryStream> streams = new LinkedList<GeometryStream>();

                for (IOreDistribution dist : cfg.getOreDistributions())
                {
                    dist.generate(request.world, request.chunkX, request.chunkZ);
                    GeometryStream stream = dist.getDebuggingGeometry(request.world, request.chunkX, request.chunkZ);

                    if (stream != null)
                    {
                        streams.add(stream);
                    }
                    dist.cull();
                }

                return new GeometryData(request, streams);
            }
        }
    }

    public static void onPopulateChunk(World world, int chunkX, int chunkZ, Random rand) {
    	WorldConfig cfg = getWorldConfig(world);
    	int range = (cfg.deferredPopulationRange + 15) / 16;
    	//for (int iX = chunkX - range; iX <= chunkX + range; ++iX)
        //{
            //for (int iZ = chunkZ - range; iZ <= chunkZ + range; ++iZ)
            //{
            	//if (allNeighborsPopulated(world, iX, iZ, range)) {
            		//CustomOreGenBase.log.info("[" + iX + "," + iZ + "]: POPULATING");
            		populateDistributions(cfg.getOreDistributions(), world, chunkX, chunkZ);
            	//}
            //}
        //}
    }

    private static boolean allNeighborsPopulated(World world, int chunkX, int chunkZ, int range) {
    	int area = 4 * range * (range + 1) + 1;
    	int neighborCount = 0;
        for (int iX = chunkX - range; iX <= chunkX + range; ++iX)
        {
            for (int iZ = chunkZ - range; iZ <= chunkZ + range; ++iZ)
            {
            	if (chunkHasBeenPopulated(world, iX, iZ)) 
            	{
            		//CustomOreGenBase.log.info("[" + iX + "," + iZ + "]: populated neighbor");
            		neighborCount++;
            	}
            }
        }
		return neighborCount == area; 
	}

	private static boolean chunkHasBeenPopulated(World world, int chunkX, int chunkZ) {
		return world.getChunk(chunkX, chunkZ).getStatus().isAtLeast(ChunkStatus.FEATURES);
	}

	public static boolean checkIfServerChanged(MinecraftServer currentServer, WorldInfo worldInfo)
    {
        if (_server == currentServer)
        {
            return false;
        }
        else
        {
            if (currentServer != null && worldInfo == null)
            {
                if (currentServer.getWorlds()== null)
                {
                    return false;
                }

                for (ServerWorld world : currentServer.getWorlds()) {
                    if (world != null)
                    {
                        worldInfo = world.getWorldInfo();
                    }

                    if (worldInfo != null)
                    {
                        break;
                    }                	
                }
                
            }

            if (worldInfo == null)
            {
                return false;
            }

            onServerChanged(currentServer, worldInfo);
            return true;
        }
    }

    public static void onServerChanged(MinecraftServer server, WorldInfo worldInfo)
    {
        _worldConfigs.clear();
        WorldConfig.loadedOptionOverrides[1] = WorldConfig.loadedOptionOverrides[2] = null;

        _server = server;
        CustomOreGenBase.log.debug("Server world changed to " + worldInfo.getWorldName());
        File f = new File(_server.getWorld(DimensionType.OVERWORLD).getSaveHandler().getWorldDirectory().getAbsolutePath());
        WorldConfig config = null;

        while (config == null)
        {
            try
            {
                config = new WorldConfig(worldInfo, f);
                validateOptions(config.getConfigOptions(), false);
                validateDistributions(config.getOreDistributions(), false);
            }
            catch (Exception var7)
            {
                if (!onConfigError(var7))
                {
                    break;
                }

                config = null;
            }
        }
    }

    @OnlyIn(Dist.CLIENT)
    public static void addOptionsButtonToGui(CreateWorldScreen gui, List<Widget> buttonList, Consumer<Widget> add)
    {
        if (gui == null)
        {
            _optionsGuiButton = null;
        }
        else
        {
            GuiCustomOreGenSettings settingsGui = new GuiCustomOreGenSettings(gui);
            _optionsGuiButton = new Button(150, 100, 150, 20, "Custom Ore Generation...", (button) -> {
            	gui.getMinecraft().displayGuiScreen(settingsGui);
            });

            Button button1 = _optionsGuiButton;
            
            if (!buttonList.contains(button1))
            {
                button1.x = (gui.width - button1.getWidth()) / 2;
                button1.y = 165;
                add.accept(button1);
            }
        }
    }
    
    @OnlyIn(Dist.CLIENT)
    public static void updateOptionsButtonVisibility(CreateWorldScreen gui) {
    	_optionsGuiButton.visible = !(Boolean)ObfuscationReflectionHelper.getPrivateValue(CreateWorldScreen.class, gui, "inMoreWorldOptionsDisplay");
    }
    
	public static void chunkForced(World world, ChunkPos location) {
		if (forcingChunk) { // prevent infinite recursion when there are multiple chunk loaders
			return;
		}
		forcingChunk = true;
		
		WorldConfig cfg = getWorldConfig(world);
		int radius = (cfg.deferredPopulationRange + 15) / 16;
        
        for (int cX = location.x - radius; cX <= location.x + radius; ++cX)
        {
            for (int cZ = location.z - radius; cZ <= location.z + radius; ++cZ)
            {
            	if (cX != location.x && cZ != location.z) {
            		world.getChunk(cX, cZ);
            	}
            }
        }
        
        forcingChunk = false;
	}
}


<|start_filename|>src/main/java/CustomOreGen/Server/WorldConfig.java<|end_filename|>
package CustomOreGen.Server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import com.google.common.base.Objects;
import com.google.common.collect.Maps;

import CustomOreGen.CustomOreGenBase;
import CustomOreGen.Config.ConfigParser;
import CustomOreGen.Config.PropertyIO;
import CustomOreGen.Util.BiomeDescriptor;
import CustomOreGen.Util.BlockDescriptor;
import CustomOreGen.Util.CIStringMap;
import CustomOreGen.Util.MapCollection;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.World;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.EndChunkGenerator;
import net.minecraft.world.gen.FlatChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.NetherChunkGenerator;
import net.minecraft.world.gen.OverworldChunkGenerator;
import net.minecraft.world.storage.WorldInfo;

public class WorldConfig
{
	@SuppressWarnings("unchecked")
	public static Collection<ConfigOption>[] loadedOptionOverrides = new Collection[3];
    public final World world;
    public final WorldInfo worldInfo;
    public final File globalConfigDir;
    public final File worldBaseDir;
    public final File dimensionDir;
    public int deferredPopulationRange;
    public boolean debuggingMode;
    public boolean vanillaOreGen;
    public boolean custom;
    private Map<String,IOreDistribution> oreDistributions;
    private Map<String,ConfigOption> configOptions;
    private Map<String,String> loadedOptions;
    private Map<String,Object> worldProperties;
    private Map<String,BiomeDescriptor> biomeSets;
	private BlockDescriptor equivalentBlockDescriptor;
	private int idCounter;

    public static WorldConfig createEmptyConfig()
    {
        try
        {
            return new WorldConfig((File)null, (WorldInfo)null, (File)null, (World)null, (File)null);
        }
        catch (Exception var1)
        {
            throw new RuntimeException(var1);
        }
    }

    public WorldConfig() throws IOException, ParserConfigurationException, SAXException
    {
        this(CustomOreGenBase.getConfigDir(), (WorldInfo)null, (File)null, (World)null, (File)null);
    }

    public WorldConfig(WorldInfo worldInfo, File worldBaseDir) throws IOException, ParserConfigurationException, SAXException
    {
        this(CustomOreGenBase.getConfigDir(), worldInfo, worldBaseDir, (World)null, (File)null);
    }

    public WorldConfig(World world) throws IOException, ParserConfigurationException, SAXException
    {
        this(CustomOreGenBase.getConfigDir(), (WorldInfo)null, (File)null, world, (File)null);
    }

    private WorldConfig(File globalConfigDir, WorldInfo worldInfo, File worldBaseDir, World world, File dimensionDir) throws IOException, ParserConfigurationException, SAXException
    {
        this.deferredPopulationRange = 0;
        this.debuggingMode = false;
        this.vanillaOreGen = false;
        this.oreDistributions = new LinkedHashMap<String,IOreDistribution>();
        this.configOptions = new CIStringMap<ConfigOption>(new LinkedHashMap<String, ConfigOption>());
        this.loadedOptions = new CIStringMap<String>(new LinkedHashMap<String, String>());
        this.worldProperties = new CIStringMap<Object>(new LinkedHashMap<String, Object>());
        this.biomeSets = new CIStringMap<BiomeDescriptor>();

        if (world != null)
        {
            if (world instanceof ServerWorld)
            {
                worldBaseDir = ((ServerWorld)world.getWorld()).getSaveHandler().getWorldDirectory();
            }
            else
            {
                worldBaseDir = null;
            }

            dimensionDir = world.getDimension().getType().getDirectory(worldBaseDir);
            if (Objects.equal(dimensionDir, worldBaseDir)) {
            	dimensionDir = new File(worldBaseDir, "DIM0");
            	if (!dimensionDir.exists())
                	dimensionDir.mkdir();
            }
            
            worldInfo = world.getWorldInfo();
        }
        
        this.world = world;
        this.worldInfo = worldInfo;
        populateWorldProperties(this.worldProperties, world, worldInfo);
        this.worldBaseDir = worldBaseDir;
        this.dimensionDir = dimensionDir;
        this.globalConfigDir = globalConfigDir;

        if (dimensionDir != null)
        {
            CustomOreGenBase.log.info("Loading config data for dimension \'" + dimensionDir + "\' ...");
        }
        else
        {
            if (globalConfigDir == null)
            {
                return;
            }

            CustomOreGenBase.log.info("Loading global config \'" + globalConfigDir + "\' ...");
        }

        File configFile = null;
        File[] configFileList = new File[3];
        int configFileDepth = this.buildFileList(CustomOreGenBase.BASE_CONFIG_FILENAME, configFileList, true);

        if (configFileDepth >= 0) 
        {
        	configFile = configFileList[configFileDepth];
        } 
        else 
        {
        	File defaultConfigFile = new File(globalConfigDir, CustomOreGenBase.DEFAULT_BASE_CONFIG_FILENAME);
        	if (defaultConfigFile.exists()) 
        	{
        		configFile = defaultConfigFile;
        		configFileDepth = 0;
        	}
        	else 
        	{
        		if (dimensionDir != null)
        		{
        			CustomOreGenBase.log.warn("No config file found for dimension \'" + dimensionDir + "\' at any scope!");
        		}
        		else
        		{
        			CustomOreGenBase.log.info("No global config file found.");
        		}
        	}
        }
        
        if (configFile != null) {
            File[] optionsFileList = new File[3];
            this.buildFileList(CustomOreGenBase.OPTIONS_FILENAME, optionsFileList, false);
            File optionsFile = optionsFileList[2];
            ConfigOption vangen;

            for (int defpopOption = configFileDepth; defpopOption < optionsFileList.length; ++defpopOption)
            {
                loadOptions(optionsFileList[defpopOption], loadedOptionOverrides[defpopOption], this.loadedOptions);
            }

            (new ConfigParser(this)).parseFile(configFile);
            ConfigOption option;

            Map<String,String> saveLevelOptions = new LinkedHashMap<String, String>();
            if (optionsFileList[1] != null) {
            	if (!optionsFileList[1].exists()) {
            		loadOptions(optionsFileList[0], loadedOptionOverrides[0], saveLevelOptions);
            		saveOptions(optionsFileList[1], saveLevelOptions);
            	} else {
            		loadOptions(optionsFileList[1], null, saveLevelOptions);
            	}
            }
            
            if (optionsFile != null)
            {
            	putOptions(this.configOptions.values(), this.loadedOptions);
            	if (world != null) {
            		Map<String,String> dimLevelOptions = setdiffOptions(this.loadedOptions, saveLevelOptions);
            		loadOptions(optionsFile, loadedOptionOverrides[2], dimLevelOptions);
            		saveOptions(optionsFile, dimLevelOptions);
            	}
            }

            option = (ConfigOption)this.configOptions.get("deferredPopulationRange");

            if (option != null && option instanceof NumericOption)
            {
                Double var18 = (Double)option.getValue();
                this.deferredPopulationRange = var18 != null && var18.doubleValue() > 0.0D ? (int)Math.ceil(var18.doubleValue()) : 0;
            }
            else
            {
                CustomOreGenBase.log.warn("Numeric Option \'" + option + "\' not found in config file - defaulting to \'" + this.deferredPopulationRange + "\'.");
            }

            option = (ConfigOption)this.configOptions.get("debugMode");

            if (option != null && option instanceof ChoiceOption)
            {
                String debugValue = (String)option.getValue();
                this.debuggingMode = debugValue == null ? false : debugValue.equalsIgnoreCase("on") || debugValue.equalsIgnoreCase("true");
            }
            else
            {
                CustomOreGenBase.log.warn("Choice Option \'" + option + "\' not found in config file - defaulting to \'" + this.debuggingMode + "\'.");
            }

            vangen = (ConfigOption)this.configOptions.get("vanillaOreGen");

            if (vangen != null && vangen instanceof ChoiceOption)
            {
                String value = (String)vangen.getValue();
                this.vanillaOreGen = value == null ? false : value.equalsIgnoreCase("on") || value.equalsIgnoreCase("true");
            }
            else
            {
                CustomOreGenBase.log.warn("Choice Option \'" + vangen + "\' not found in config file - defaulting to \'" + this.vanillaOreGen + "\'.");
            }
            
        }
    }

	private Map<String, String> setdiffOptions(Map<String, String> x, Map<String, String> y) {
		return new LinkedHashMap<String, String>(Maps.difference(x, y).entriesOnlyOnLeft());
	}

	private void loadOptions(File file, Collection<ConfigOption> overrides, Map<String, String> map) throws FileNotFoundException, IOException {
		if (file != null && file.exists())
        {
            PropertyIO.load(map, new FileInputStream(file));
        }

        if (overrides != null)
        {
        	putOptions(overrides, map);
        }
	}

	private void putOptions(Collection<ConfigOption> options, Map<String, String> map) {
    	for (ConfigOption option : options) {
    		if (option.getValue() != null) {
    			map.put(option.getName(), option.getValue().toString());
    		}
    	}
	}

	private void saveOptions(File optionsFile, Map<String, String> options) throws IOException {
    	optionsFile.createNewFile();
        String header = CustomOreGenBase.getDisplayString() + " Config Options";
        PropertyIO.save(options, new FileOutputStream(optionsFile), header);
	}

	private int buildFileList(String fileName, File[] files, boolean mustExist)
    {
        if (files == null)
        {
            files = new File[3];
        }

        if (this.globalConfigDir != null)
        {
            files[0] = new File(this.globalConfigDir, fileName);
        }

        if (this.worldBaseDir != null)
        {
            files[1] = new File(this.worldBaseDir, fileName);
        }

        if (this.dimensionDir != null && dimensionDir.exists())
        {
            files[2] = new File(this.dimensionDir, fileName);
        }

        for (int i = files.length - 1; i >= 0; --i)
        {
            if (files[i] != null && (!mustExist || files[i].exists()))
            {
                return i;
            }
        }

        return -1;
    }

    private static void populateWorldProperties(Map<String,Object> properties, World world, WorldInfo worldInfo)
    {
        properties.put("world", worldInfo == null ? "" : worldInfo.getWorldName());
        properties.put("world.seed", worldInfo == null ? 0L : worldInfo.getSeed());
        properties.put("world.version", worldInfo == null ? 0 : worldInfo.getSaveVersion());
        properties.put("world.isHardcore", worldInfo == null ? false : worldInfo.isHardcore());
        properties.put("world.hasFeatures", worldInfo == null ? false : worldInfo.isMapFeaturesEnabled());
        properties.put("world.hasCheats", worldInfo == null ? false : worldInfo.areCommandsAllowed());
        properties.put("world.gameMode", worldInfo == null ? "" : worldInfo.getGameType().getName());
        properties.put("world.gameMode.id", worldInfo == null ? 0 : worldInfo.getGameType().getID());
        properties.put("world.type", worldInfo == null ? "" : worldInfo.getWorldName());
        properties.put("world.type.version", worldInfo == null ? 0 : worldInfo.getSaveVersion());
        String genName = "RandomLevelSource";
        String genClass = "ChunkProviderOverworld";

        if (world != null)
        {
            ChunkGenerator<? extends GenerationSettings> chunkGenerator = world.getChunkProvider().getChunkGenerator();
            genName = chunkGenerator.toString();
            genClass = chunkGenerator.getClass().getSimpleName();

            if (chunkGenerator instanceof OverworldChunkGenerator)
            {
                genClass = "ChunkProviderOverworld";
            }
            else if (chunkGenerator instanceof FlatChunkGenerator)
            {
                genClass = "ChunkProviderFlat";
            }
            else if (chunkGenerator instanceof NetherChunkGenerator)
            {
                genClass = "ChunkProviderHell";
            }
            else if (chunkGenerator instanceof EndChunkGenerator)
            {
                genName = "EndRandomLevelSource";
                genClass = "ChunkProviderEnd";
            }
        }

        properties.put("dimension.generator", genName);
        properties.put("dimension.generator.class", genClass);
        properties.put("dimension", world == null ? "" : world.getDimension().getType().getRegistryName());
        properties.put("dimension.id", world == null ? 0 : world.getDimension().getType().getId());
        properties.put("dimension.isSurface", world == null ? false : world.getDimension().isSurfaceWorld());
        properties.put("dimension.hasNoSky", world == null ? false : world.getDimension().hasSkyLight());
        properties.put("dimension.groundLevel", world == null ? 0 : world.getDimension().getHorizon());
        properties.put("dimension.actualHeight", world == null ? 0 : world.getActualHeight());
        properties.put("dimension.height", world == null ? 0 : world.getHeight());
        properties.put("age", false);
    }

    public Collection<IOreDistribution> getOreDistributions()
    {
        return this.oreDistributions.values();
    }

    public Collection<IOreDistribution> getOreDistributions(String namePattern)
    {
        LinkedList<IOreDistribution> matches = new LinkedList<IOreDistribution>();

        if (namePattern != null)
        {
            Pattern pattern = Pattern.compile(namePattern, 2);
            Matcher matcher = pattern.matcher("");
            for (IOreDistribution dist : this.oreDistributions.values()) {
            	matcher.reset(dist.toString());

                if (matcher.matches())
                {
                    matches.add(dist);
                }
            }
        }

        return Collections.unmodifiableCollection(matches);
    }

    public ConfigOption getConfigOption(String optionName)
    {
        return this.configOptions.get(optionName);
    }

    public Collection<ConfigOption> getConfigOptions()
    {
    	return new MapCollection<String,ConfigOption>(this.configOptions) {
     	    protected String getKey(ConfigOption v)
    	    {
    	        return v.getName();
    	    }
    	};
    }

    public Collection<ConfigOption> getConfigOptions(String namePattern)
    {
        LinkedList<ConfigOption> matches = new LinkedList<ConfigOption>();

        if (namePattern != null)
        {
            Pattern pattern = Pattern.compile(namePattern, 2);
            Matcher matcher = pattern.matcher("");
            for (ConfigOption option : this.configOptions.values()) {
            	matcher.reset(option.getName());

                if (matcher.matches())
                {
                    matches.add(option);
                }
            }
        }

        return Collections.unmodifiableCollection(matches);
    }

    public String loadConfigOption(String optionName)
    {
        return (String)this.loadedOptions.get(optionName);
    }

    public Object getWorldProperty(String propertyName)
    {
        return this.worldProperties.get(propertyName);
    }
    
	public BiomeDescriptor getBiomeSet(String namePattern) {
        return this.biomeSets.get(namePattern);
	}	

	public BlockDescriptor getEquivalentBlockDescriptor() {
		if (this.equivalentBlockDescriptor == null) {
			this.equivalentBlockDescriptor = this.makeEquivalentBlockDescriptor();
		}
		return this.equivalentBlockDescriptor;
	}

	private BlockDescriptor makeEquivalentBlockDescriptor() {
		double totalWeight = 0;
		for (IOreDistribution dist : this.oreDistributions.values()) {
			totalWeight += dist.getOresPerChunk();
		}
		BlockDescriptor desc = new BlockDescriptor();
		for (IOreDistribution dist : this.oreDistributions.values()) {
			BlockDescriptor oreBlock = (BlockDescriptor)dist.getDistributionSetting("OreBlock");
			desc.add(oreBlock, (float)(dist.getOresPerChunk() / totalWeight));
		}
		return desc;
	}

	public void registerDistribution(String newName, IOreDistribution distribution) {
		if (this.oreDistributions.containsKey(newName)) {
			this.oreDistributions.remove(newName); // otherwise, order is not updated
		}
		this.oreDistributions.put(newName, distribution);
	}

	public void registerBiomeSet(BiomeDescriptor biomeSet) {
		this.biomeSets.put(biomeSet.getName(), biomeSet);
	}
	
	public int nextDistributionID() {
		return this.idCounter++;
	}
}


<|start_filename|>src/main/java/CustomOreGen/Util/Localization.java<|end_filename|>
package CustomOreGen.Util;

import net.minecraft.client.resources.I18n;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;

@OnlyIn(Dist.CLIENT)
public class Localization {
	public static String maybeLocalize(String key, String defaultValue) {
		String value = defaultValue;
		String localizedValue = I18n.format(key);
    	if (!localizedValue.equals(key)) {
    		value = localizedValue;
    	}
    	return value;
	}
}


<|start_filename|>src/main/java/CustomOreGen/Util/BlockDescriptor.java<|end_filename|>
package CustomOreGen.Util;

import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.regex.Pattern;

import com.google.common.collect.ImmutableList;

import CustomOreGen.Server.DistributionSettingMap.Copyable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.Tag;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistries;

public class BlockDescriptor implements Copyable<BlockDescriptor>
{
	public static class BlockInfo
    {
        private BlockState blockState;
        private boolean ignoreState;
        private CompoundNBT nbt;
        
        private BlockInfo(BlockState blockState, CompoundNBT nbt, boolean ignoreState) {
        	this.blockState = blockState;
        	this.nbt = nbt;
        	this.ignoreState = ignoreState;
        }
        
        public BlockInfo(BlockState blockState, CompoundNBT nbt) {
	        this(blockState, nbt, false);
        }
        
        public BlockInfo(Block block, CompoundNBT nbt) {
        	this(block.getDefaultState(), nbt, true);
        }
        
        @Override
        public int hashCode()
        {
			int code = this.ignoreState ? this.blockState.getBlock().hashCode() : this.blockState.hashCode();
            if (this.nbt != null)
            	code ^= nbt.hashCode();
            return code;
        }
		
        @Override
        public boolean equals(Object o)
        {
            if (!(o instanceof BlockInfo)) return false;
            BlockInfo ok = (BlockInfo)o;
            if (this.ignoreState != ok.ignoreState) return false;
            if (this.ignoreState) {
            	if (this.blockState.getBlock() != ok.blockState.getBlock()) return false;
            } else if (!this.blockState.equals(ok.blockState)) return false;
            if ((this.nbt != null || ok.nbt != null) && (this.nbt == null || !this.nbt.equals(ok.nbt))) return false;
            return true;
        }
        public BlockState getBlockState() {
        	return this.blockState;
        }
        public CompoundNBT getNBT() {
			return this.nbt;
		}
        public boolean ignoresState() {
        	return this.ignoreState;
        }
        public Block getBlock() {
        	return this.blockState.getBlock();
        }
    }
	
	private static class Match
    {
		public final float weight;
		
		public Match(float weight) {
			super();
			this.weight = weight;
		}
    }
	
    protected LinkedList<Descriptor> _descriptors = new LinkedList<Descriptor>();
    protected Map<BlockInfo,Match> _matches = new Hashtable<BlockInfo,Match>();
    protected boolean _compiled = false;
    
    public BlockDescriptor()
    {
        this.clear();
    }

    public BlockDescriptor(String descriptor)
    {
        this.set(descriptor);
    }

    public BlockDescriptor(Block stone) {
		this.set(stone);
	}

	public void copyFrom(BlockDescriptor source)
    {
        this._descriptors = new LinkedList<Descriptor>(source._descriptors);
        this._matches = new Hashtable<BlockInfo, Match>(source._matches);
        this._compiled = source._compiled;
    }

	public BlockDescriptor set(Block block) {
		return this.set(ForgeRegistries.BLOCKS.getKey(block).toString());
	}
	
    public BlockDescriptor set(String descriptor)
    {
        this.clear();

        if (descriptor != null)
        {
            this.add(descriptor);
        }

        return this;
    }

    public BlockDescriptor add(String descriptor)
    {
        return this.add(descriptor, 1.0F, false, false, false, null);
    }

    public BlockDescriptor add(String descriptor, float weight, CompoundNBT nbt) {
    	return this.add(descriptor, weight, false, false, false, nbt);
    }
    
    public BlockDescriptor add(String descriptor, float weight, boolean describesOre, boolean matchFirst, boolean regexp, CompoundNBT nbt)
    {
        if (descriptor != null && weight != 0.0F)
        {
            this._compiled = false;
            this._descriptors.add(new Descriptor(descriptor, weight, describesOre, matchFirst, regexp, nbt));
        }

        return this;
    }

    public BlockDescriptor clear()
    {
        this._compiled = false;
        this._descriptors.clear();
        return this;
    }

    public List<Descriptor> getDescriptors()
    {
        return Collections.unmodifiableList(this._descriptors);
    }

    private void add(Block block, float weight)
    {
    	this.add(new BlockInfo(block, null), weight);
    }
    
    private void add(Block block, CompoundNBT nbt, float weight)
    {
    	this.add(new BlockInfo(block, nbt), weight);
    }
    
    private void add(BlockState blockState, CompoundNBT nbt, float weight)
    {
    	this.add(new BlockInfo(blockState, nbt), weight);
    }
    
    private void add(BlockInfo blockInfo, float weight)
    {
    	BlockState blockState = blockInfo.getBlockState();
    	Block block = blockState.getBlock();
    	CompoundNBT nbt = blockInfo.getNBT();
    	if (nbt != null && !block.hasTileEntity(blockState)) {
    		throw new IllegalArgumentException("NBT specified, but matching block " + 
    				block.getTranslationKey() + " lacks tile entity");
    	}
        if (weight != 0.0F)
        {
            Match match = this._matches.get(blockInfo);

            if (match != null)
            {
                match = new Match(match.weight + weight);
            } else 
            {
            	match = new Match(weight);
            }

            this._matches.put(blockInfo, match);
        }
    }

	public void add(BlockDescriptor desc, float weight) {
		for (Descriptor d : desc._descriptors) {
			this.add(d.description, d.weight * weight, d.describesOre, d.matchFirst, d.regexp, d.nbt);
		}
	}

    protected void compileMatches()
    {
        if (!this._compiled)
        {
            this._compiled = true;
            this._matches.clear();
            
            for (Descriptor desc : this._descriptors) {
            	if (desc.describesOre) {
            		ResourceLocation tagName = new ResourceLocation(desc.description);
            		Tag<Block> tag = BlockTags.getCollection().get(tagName);
            		if(tag == null) {
            			tag = BlockTags.getCollection().get(new ResourceLocation("forge",desc.description));
            		}
            		if(tag == null) {
            			throw new RuntimeException(desc.description + " does not match any tags.");
            		}
            		for (Block block : tag.getAllElements()) {
            			this.add(block, desc.weight);
            			if (desc.matchFirst) {
            				break;
            			}
            		}
            	} else if (desc.regexp) {
            		for (Block block : ForgeRegistries.BLOCKS) {
            			//desc.regexMatch compares against states
                    	float[] weights = desc.regexMatch(block);
                    	this.add(block, desc.nbt, weights[0]);
                    	if (weights[0] > 0 && desc.matchFirst) {
                    		break;
                    	}
                    	boolean matched = false;
                    	for (int m = 0; m < weights.length && !matched; ++m)
                    	{
                    		if (weights[m] > 0) {
                    			if(m == 0 )
                    				this.add(block, desc.nbt, weights[m]);
                    			else {
                                	ImmutableList<BlockState> states = block.getStateContainer().getValidStates();
                                	this.add(states.get(m-1), desc.nbt, weights[m]);
                    			}
                    			if (desc.matchFirst) {
                    				matched = true;
                    			}
                    		}
                    	}
                    	if (matched) {
                    		break;
                    	}
                    }
            	} else {
            		Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(desc.getBlockName()));
            		/*
            		 * TODO: support block state on 'desc' and construct + register here 
            		 */
            		if (block != null)
            		  this.add(block, desc.nbt, desc.weight);
            	}
            }
        }
    }

   	public Map<BlockInfo,Match> getMatches()
    {
        this.compileMatches();
        return Collections.unmodifiableMap(this._matches);
    }

    public float getWeight(BlockState blockState, CompoundNBT nbt)
    {
        this.compileMatches();
        float value = 0.0F;
        Match noStateValue = this._matches.get(new BlockInfo(blockState.getBlock(), nbt));

        if (noStateValue != null)
        {
            value = noStateValue.weight;
        }

        Match stateValue = this._matches.get(new BlockInfo(blockState, nbt));

        if (stateValue != null)
        {
        	value += stateValue.weight;
        }

        return value;
    }

    public boolean matchesBlock(BlockState blockState, Random rand)
    {
        float weight = this.getWeight(blockState, null);

        if (weight <= 0.0F)
        {
            return false;
        }
        else if (weight < 1.0F)
        {
            if (rand == null)
            {
                rand = new Random();
            }

            return rand.nextFloat() < weight;
        }
        else
        {
            return true;
        }
    }

    public BlockInfo getMatchingBlock(Random rand)
    {
        this.compileMatches();
        float value = -1.0F;
        
        for (Entry<BlockInfo,Match> entry : _matches.entrySet()) {
        	float weight = entry.getValue().weight;
            BlockInfo info = entry.getKey();
            
            if (weight > 0.0F)
            {
                if (weight >= 1.0F)
                {
                    return info;
                }

                if (value < 0.0F)
                {
                    if (rand == null)
                    {
                        rand = new Random();
                    }

                    value = rand.nextFloat();
                }

                value -= weight;

                if (value < 0.0F)
                {
                    return info;
                }
            }
        }

        return null;
    }

    public float getTotalMatchWeight()
    {
        this.compileMatches();
        float weight = 0.0F;
        
        for (Match m : _matches.values()) {
        	if (m.weight > 0.0F)
            {
                weight += m.weight;
            }
        }

        return weight;
    }

    public String toString()
    {
        switch (this._descriptors.size())
        {
            case 0:
                return "[no blocks]";

            case 1:
                return ((Descriptor)this._descriptors.get(0)).toString();

            default:
                return this._descriptors.toString();
        }
    }

    public String[] toDetailedString()
    {
        this.compileMatches();
        String[] breakdown = new String[this._matches.size() + 1];
        breakdown[0] = this._matches.size() + " block matches";

        if (this._matches.size() > 0)
        {
            breakdown[0] += ':';
        }

        int i = 1;

        for (Entry<BlockInfo,Match> entry : _matches.entrySet()) {
        	float weight = entry.getValue().weight;
            Block block = entry.getKey().getBlock();
            
            breakdown[i] = ForgeRegistries.BLOCKS.getKey(block).toString();
            breakdown[i] += " (" + weight + ")";
            
            ++i;
        }

        return breakdown;
    }
    
    private class Descriptor
    {
        public final String description;
        public final float weight;
        public final boolean describesOre;
        public final boolean matchFirst;
        public final boolean regexp;
        public final CompoundNBT nbt;
        private Pattern pattern = null;
        
        public Descriptor(String description, float weight, boolean describesOre, boolean matchFirst, boolean regexp, CompoundNBT nbt)
        {
            this.description = description;
            this.weight = weight;
			this.describesOre = describesOre;
			this.matchFirst = matchFirst;
			this.regexp = regexp;
			this.nbt = nbt;
        }

        public Pattern getPattern()
        {
            if (this.pattern == null)
            {
                this.pattern = Pattern.compile(this.description, Pattern.CASE_INSENSITIVE);
            }

            return this.pattern;
        }
        
        public String getBlockName() {
        	boolean hasMeta = this.description.indexOf(':') != this.description.lastIndexOf(':');
        	if (hasMeta) {
        		return this.description.substring(0, this.description.lastIndexOf(':'));
        	} else {
        		return this.description;
        	}
        }
        
        public float[] regexMatch(Block block)
        {
            float[] weights = new float[1];
            String name = block.getRegistryName().toString();
        	
            if (!this.getPattern().matcher(name).matches())
            {
            	ImmutableList<BlockState> states = block.getStateContainer().getValidStates();
                weights = new float[states.size() + 1];
                int m=1;
            	for (BlockState state : states)
            	{
            		//TODO: this might be improved
            		if (this.getPattern().matcher(state.toString()).matches())
            		{
            			weights[m] += this.weight;
            			m++;
            		}
            	}
            }
            else
            {
            	weights[0] += this.weight;
            }
            return weights;
        }
        
        public String toString()
        {
            return this.description + " - " + Float.toString(this.weight);
        }
    }

	public boolean isEmpty() {
		return this._descriptors.isEmpty();
	}
}


<|start_filename|>src/main/java/CustomOreGen/Config/ValidatorBlockDescriptor.java<|end_filename|>
package CustomOreGen.Config;

import org.w3c.dom.Node;

import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.JsonToNBT;

public class ValidatorBlockDescriptor extends ValidatorNode
{
    public String blocks = null;
    public float weight = 1.0F;
	public CompoundNBT nbt;
	
    protected ValidatorBlockDescriptor(ValidatorNode parent, Node node)
    {
        super(parent, node);
    }

    protected boolean validateChildren() throws ParserException
    {
        super.validateChildren();
        this.blocks = this.validateRequiredAttribute(String.class, "Block", true);
        this.weight = this.validateNamedAttribute(Float.class, "Weight", this.weight, true);
        String nbtJson = this.validateNamedAttribute(String.class, "NBT", null, true);
        if (nbtJson != null) {
        	try {
        		this.nbt = JsonToNBT.getTagFromJson(nbtJson);
			} catch (Exception e) {
				throw new ParserException("Failed to parse JSON", e);
			}
        }
        return true;
    }
    
    public static class Factory implements IValidatorFactory<ValidatorBlockDescriptor>
    {
        public ValidatorBlockDescriptor createValidator(ValidatorNode parent, Node node)
        {
            return new ValidatorBlockDescriptor(parent, node);
        }
    }

}


<|start_filename|>src/main/java/CustomOreGen/Server/ConfigErrorDialog.java<|end_filename|>
package CustomOreGen.Server;

import java.awt.Button;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.Point;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.BoxLayout;

public class ConfigErrorDialog implements WindowListener, ActionListener
{
    private boolean _waiting = false;
    private Dialog _dialog = null;
    private Button _abort = null;
    private Button _retry = null;
    private Button _ignore = null;
    private int _returnVal = 0;

    public int showDialog(Frame parentWindow, Throwable error)
    {
        if (this._dialog != null)
        {
            throw new IllegalStateException("CustomOreGen Config Error Dialog is already open!");
        }
        else
        {
            this._dialog = new Dialog(parentWindow, "CustomOreGen Config Error", false);
            this._dialog.addWindowListener(this);
            TextArea text = new TextArea(this.getMessage(error), 30, 120, 1);
            text.setEditable(false);
            text.setBackground(Color.WHITE);
            text.setFont(new Font("Monospaced", 0, 12));
            this._dialog.add(text);
            Panel buttonPanel = new Panel();
            this._abort = new Button("Abort");
            this._abort.addActionListener(this);
            buttonPanel.add(this._abort);
            this._retry = new Button("Retry");
            this._retry.addActionListener(this);
            buttonPanel.add(this._retry);
            this._ignore = new Button("Ignore");
            this._ignore.addActionListener(this);
            buttonPanel.add(this._ignore);
            buttonPanel.setLayout(new BoxLayout(buttonPanel, 0));
            this._dialog.add(buttonPanel);
            this._dialog.setLayout(new BoxLayout(this._dialog, 1));
            this._dialog.pack();
            Point loc = parentWindow.getLocation();
            Dimension parentSize = parentWindow.getSize();
            Dimension size = this._dialog.getSize();
            loc.x += (parentSize.width - size.width) / 2;
            loc.y += (parentSize.height - size.height) / 2;
            this._dialog.setLocation(loc);
            this._waiting = true;
            this._returnVal = 0;
            this._dialog.setVisible(true);

            while (this._waiting)
            {
            }

            this._abort = null;
            this._retry = null;
            this._ignore = null;
            this._dialog.setVisible(false);
            this._dialog.dispose();
            this._dialog = null;
            return this._returnVal;
        }
    }

    protected String getMessage(Throwable error)
    {
        StringBuilder msg = new StringBuilder();
        msg.append("CustomOreGen has detected an error while trying to load its config files.\n");
        msg.append("At this time you may: \n");
        msg.append("  (1) Abort loading and close Minecraft (click \'Abort\').\n");
        msg.append("  (2) Try to fix the error and then reload the config files (click \'Retry\').\n");
        msg.append("  (3) Ignore the error and continue without loading the config files (click \'Ignore\').\n");
        msg.append("It is strongly recommended that you do not ignore the error.\n");
        msg.append('\n');
        msg.append("------ Error Message ------\n\n");
        msg.append(error.toString());
        msg.append("\n\n");

        for (Throwable th = error.getCause(); th != null; th = th.getCause())
        {
            msg.append("-------- Caused By --------\n\n");
            msg.append(th.toString());
            msg.append("\n\n");
        }

        return msg.toString();
    }

    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() == this._abort)
        {
            this._returnVal = 0;
            this._waiting = false;
        }
        else if (e.getSource() == this._retry)
        {
            this._returnVal = 1;
            this._waiting = false;
        }
        else if (e.getSource() == this._ignore)
        {
            this._returnVal = 2;
            this._waiting = false;
        }
    }

    public void windowClosing(WindowEvent e)
    {
        this._waiting = false;
    }

    public void windowActivated(WindowEvent e) {}

    public void windowClosed(WindowEvent e) {}

    public void windowDeactivated(WindowEvent e) {}

    public void windowDeiconified(WindowEvent e) {}

    public void windowIconified(WindowEvent e) {}

    public void windowOpened(WindowEvent e) {}
}


<|start_filename|>src/main/java/CustomOreGen/Client/GeometryRenderer.java<|end_filename|>
package CustomOreGen.Client;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.lwjgl.opengl.GL11;

import CustomOreGen.Util.IGeometryBuilder;
import CustomOreGen.Util.Transform;

public class GeometryRenderer implements IGeometryBuilder
{
    private int _curBufferIdx = -1;
    private Transform _posTrans = null;
    private Transform _nmlTrans = null;
    private float[] _normal = null;
    private float[] _color = null;
    private float[] _texcoords = null;
    private int _texture = -1;
    private Transform _texTrans = null;
    private PrimitiveType _primitive = null;
    private int[] _implicitRefs = null;
    private Map<String,Integer> _textureMap = null;
    private ArrayList<VertexBuffer> _vertexBuffers = new ArrayList<VertexBuffer>();
    private VertexBuffer _dbgNormalLines = null;
    private long[] _vertexIndexMap = new long[256];
    private int _vertexCount = 0;
    private int _flushedVertexCount = 0;
    private int _processedVertexCount = 0;
    private boolean _polygonParity = false;

    public void mapTexture(String textureURI, int textureName)
    {
        if (this._textureMap == null)
        {
            this._textureMap = new HashMap<String, Integer>();
        }

        if (textureName < 0)
        {
            this._textureMap.remove(textureURI);
        }
        else
        {
            this._textureMap.put(textureURI, textureName);
        }
    }

    public void setPositionTransform(Transform transform)
    {
        this._posTrans = transform;

        if (transform != null && this._normal != null)
        {
            this._nmlTrans = transform.clone().inverse().transpose();
        }
        else
        {
            this._nmlTrans = null;
        }
    }

    public void setNormal(float[] normal)
    {
        if (this._normal == null != (normal == null))
        {
            this._curBufferIdx = -1;

            if (normal == null)
            {
                this._nmlTrans = null;
            }
            else if (this._posTrans != null)
            {
                this._nmlTrans = this._posTrans.clone().inverse().transpose();
            }
        }

        this._normal = normal;
    }

    public void setColor(float[] color)
    {
        if (this._color == null != (color == null))
        {
            this._curBufferIdx = -1;
        }

        this._color = color;
    }

    public void setTexture(String textureURI)
    {
        int texture = -1;

        if (textureURI != null && this._textureMap != null)
        {
            texture = this._textureMap.get(textureURI);
        }

        if (this._texcoords != null && this._texture != texture)
        {
            this._curBufferIdx = -1;
        }

        this._texture = texture;
    }

    public void setTextureTransform(Transform transform)
    {
        this._texTrans = transform;
    }

    public void setTextureCoordinates(float[] texcoords)
    {
        if (this._texture >= 0 && this._texcoords == null != (texcoords == null))
        {
            this._curBufferIdx = -1;
        }

        this._texcoords = texcoords;
    }

    public void setVertexMode(PrimitiveType primitive, int ... vertexIndices)
    {
        if (this._primitive != primitive)
        {
            this._curBufferIdx = -1;
        }

        this._primitive = primitive;
        int curIRefCount = this._implicitRefs == null ? 0 : this._implicitRefs.length;
        int newIRefCount = vertexIndices == null ? 0 : vertexIndices.length;

        if (vertexIndices != this._implicitRefs && (curIRefCount > 0 || newIRefCount > 0) && !Arrays.equals(vertexIndices, this._implicitRefs))
        {
            ;
        }

        this._implicitRefs = vertexIndices;
        this._polygonParity = false;
        this._processedVertexCount = this._vertexCount;
        this._flushedVertexCount = this._vertexCount;
    }

    public void addVertex(float[] pos)
    {
        this.addVertex(pos, (float[])null, (float[])null, (float[])null);
    }

    public void addVertex(float[] pos, float[] normal, float[] color, float[] texcoords)
    {
        if (this._curBufferIdx < 0)
        {
            this.setVertexBuffer();
        }

        VertexBuffer curBuffer = this._vertexBuffers.get(this._curBufferIdx);
        float[][] args = new float[4][];
        byte argIdx = 0;

        if (this._posTrans != null)
        {
            pos = Arrays.copyOf(pos, 3);
            this._posTrans.transformVector(pos);
        }

        int i = argIdx + 1;
        args[argIdx] = pos;

        if (this._normal != null)
        {
            if (normal == null)
            {
                normal = this._normal;
            }

            if (this._nmlTrans != null)
            {
                normal = Arrays.copyOf(normal, 3);
                this._nmlTrans.transformVector(normal);
            }

            args[i++] = normal;

            if (this._dbgNormalLines != null)
            {
                normal = Arrays.copyOf(normal, 3);
                normal[0] *= 0.1F;
                normal[1] *= 0.1F;
                normal[2] *= 0.1F;
                normal[0] += pos[0];
                normal[1] += pos[1];
                normal[2] += pos[2];
                this._dbgNormalLines.addIndex(this._dbgNormalLines.addVertex(new float[][] {pos}));
                this._dbgNormalLines.addIndex(this._dbgNormalLines.addVertex(new float[][] {normal}));
            }
        }

        if (this._color != null)
        {
            if (color == null)
            {
                color = this._color;
            }

            args[i++] = color;
        }

        if (this._texture >= 0 && this._texcoords != null)
        {
            if (texcoords == null)
            {
                texcoords = this._texcoords;
            }

            if (this._texTrans != null)
            {
                texcoords = Arrays.copyOf(texcoords, 2);
                this._texTrans.transformVector(texcoords);
            }

            args[i++] = texcoords;
        }

        int vidx = curBuffer.addVertex(args);

        if (this._vertexCount == this._vertexIndexMap.length)
        {
            this._vertexIndexMap = Arrays.copyOf(this._vertexIndexMap, this._vertexCount * 2);
        }

        this._vertexIndexMap[this._vertexCount] = (long)this._curBufferIdx << 32 | (long)vidx;
        ++this._vertexCount;
        this.processVertices();
    }

    public void addVertexRef(int vertexIndex)
    {
        if (this._curBufferIdx < 0)
        {
            this.setVertexBuffer();
        }

        if (this._vertexCount == this._vertexIndexMap.length)
        {
            this._vertexIndexMap = Arrays.copyOf(this._vertexIndexMap, this._vertexCount * 2);
        }

        if (vertexIndex >= 1 && vertexIndex <= this._vertexCount)
        {
            this._vertexIndexMap[this._vertexCount] = this._vertexIndexMap[this._vertexCount - vertexIndex];
        }
        else
        {
            this._vertexIndexMap[this._vertexCount] = -1L;
        }

        ++this._vertexCount;
        this.processVertices();
    }

    public void draw()
    {
        for (VertexBuffer buffer : this._vertexBuffers)
        {
            buffer.drawBuffer();
        }

        if (this._dbgNormalLines != null)
        {
            this._dbgNormalLines.drawBuffer();
        }
    }

    public void enableDebuggingNormalLines(boolean enable)
    {
        if (!enable)
        {
            this._dbgNormalLines = null;
        }
        else if (this._dbgNormalLines == null)
        {
            this._dbgNormalLines = new VertexBuffer(128, 1, false, false, -1);
        }
    }

    private void processVertices()
    {
        VertexBuffer curBuffer = this._curBufferIdx >= 0 ? this._vertexBuffers.get(this._curBufferIdx) : null;

        if (curBuffer != null && this._primitive != null)
        {
            byte vCount = 0;

            switch (this._primitive)
            {
                case POINT:
                    vCount = 1;
                    break;

                case LINE:
                    vCount = 2;
                    break;

                case TRIANGLE:
                case TRIANGLE_ALT:
                    vCount = 3;
                    break;

                case QUAD:
                    vCount = 4;
            }

            int iRefCount = this._implicitRefs == null ? 0 : this._implicitRefs.length;

            if (iRefCount >= vCount)
            {
                iRefCount = vCount - 1;
            }

            int iRefMax = 0;
            int groupSize;

            for (groupSize = 0; groupSize < iRefCount; ++groupSize)
            {
                if (this._implicitRefs[groupSize] > iRefMax)
                {
                    iRefMax = this._implicitRefs[groupSize];
                }
            }

            if (this._processedVertexCount - this._flushedVertexCount < iRefMax)
            {
                this._processedVertexCount = Math.min(this._flushedVertexCount + iRefMax, this._vertexCount);
            }

            groupSize = vCount - iRefCount;

            while (this._vertexCount - this._processedVertexCount >= groupSize)
            {
                VertexBuffer[] polyVertBuffers = new VertexBuffer[vCount];
                int[] polyVertIndices = new int[vCount];
                boolean valid = true;
                int i = 0;

                while (true)
                {
                    if (i < vCount)
                    {
                        long j = -1L;

                        if (i < iRefCount)
                        {
                            if (this._implicitRefs[i] >= 1)
                            {
                                j = this._vertexIndexMap[this._processedVertexCount - this._implicitRefs[i]];
                            }
                        }
                        else
                        {
                            j = this._vertexIndexMap[this._processedVertexCount + (i - iRefCount)];
                        }

                        if (j == -1L)
                        {
                            valid = false;
                        }
                        else
                        {
                            polyVertBuffers[i] = this._vertexBuffers.get((int)(j >>> 32));

                            if (!curBuffer.canCopyFrom(polyVertBuffers[i]))
                            {
                                valid = false;
                            }
                            else
                            {
                                polyVertIndices[i] = (int)(j & -1L);

                                if (polyVertIndices[i] >= 0 && polyVertIndices[i] < polyVertBuffers[i].getVertexCount())
                                {
                                    ++i;
                                    continue;
                                }

                                valid = false;
                            }
                        }
                    }

                    if (valid)
                    {
                        for (i = 0; i < vCount; ++i)
                        {
                            int bufIdx = this._primitive == PrimitiveType.TRIANGLE_ALT && this._polygonParity ? vCount - 1 - i : i;

                            if (polyVertBuffers[bufIdx] != curBuffer)
                            {
                                polyVertIndices[bufIdx] = curBuffer.copyVertex(polyVertBuffers[bufIdx], polyVertIndices[bufIdx]);
                            }

                            curBuffer.addIndex(polyVertIndices[bufIdx]);
                        }

                        this._polygonParity = !this._polygonParity;
                    }

                    this._processedVertexCount += groupSize;
                    break;
                }
            }
        }
    }

    private void setVertexBuffer()
    {
        byte renderMode = -1;

        if (this._primitive != null)
        {
            switch(this._primitive)
            {
                case POINT:
                    renderMode = 0;
                    break;

                case LINE:
                    renderMode = 1;
                    break;

                case TRIANGLE:
                case TRIANGLE_ALT:
                    renderMode = 4;
                    break;

                case QUAD:
                    renderMode = 7;
            }
        }

        boolean hasNormal = this._normal != null;
        boolean hasColor = this._color != null;
        int texture = this._texcoords == null ? -1 : this._texture;
        VertexBuffer curBuffer = this._curBufferIdx >= 0 ? this._vertexBuffers.get(this._curBufferIdx) : null;

        if (curBuffer == null || renderMode != curBuffer.renderMode || hasNormal != curBuffer.hasNormal || hasColor != curBuffer.hasColor || texture != curBuffer.texture)
        {
            VertexBuffer newBuffer = null;

            for (int i = 0; i < this._vertexBuffers.size(); ++i)
            {
                VertexBuffer buffer = this._vertexBuffers.get(i);

                if (buffer != curBuffer && renderMode == buffer.renderMode && hasNormal == buffer.hasNormal && hasColor == buffer.hasColor && texture == buffer.texture)
                {
                    this._curBufferIdx = i;
                    newBuffer = buffer;
                    break;
                }
            }

            if (newBuffer == null)
            {
                newBuffer = new VertexBuffer(renderMode > 0 ? 128 : 0, renderMode, hasNormal, hasColor, texture);
                this._curBufferIdx = this._vertexBuffers.size();
                this._vertexBuffers.add(newBuffer);
            }
        }
    }
    
    public class VertexBuffer
    {
        public final int renderMode;
        public final boolean hasNormal;
        public final boolean hasColor;
        public final int texture;
        private ByteBuffer vBuffer;
        private IntBuffer xBuffer;

        public VertexBuffer(int initialSize, int renderMode, boolean hasNormal, boolean hasColor, int texture)
        {
            this.renderMode = renderMode;
            this.hasNormal = hasNormal;
            this.hasColor = hasColor;
            this.texture = texture;

            if (initialSize <= 0)
            {
                initialSize = 1;
            }

            this.vBuffer = ByteBuffer.allocateDirect(initialSize * this.getVertexSize()).order(ByteOrder.nativeOrder());
            this.xBuffer = ByteBuffer.allocateDirect(initialSize * 4).order(ByteOrder.nativeOrder()).asIntBuffer();
            this.clear();
        }

        public void clear()
        {
            this.vBuffer.limit(0);
            this.xBuffer.limit(0);
        }

        private int getVertexSize()
        {
            int vsize = 3;

            if (this.hasNormal)
            {
                vsize += 3;
            }

            if (this.hasColor)
            {
                ++vsize;
            }

            if (this.texture >= 0)
            {
                vsize += 2;
            }

            return vsize * 4;
        }

        public int getVertexCount()
        {
            return this.vBuffer.limit() / this.getVertexSize();
        }

        public int addVertex(float[] ... data)
        {
            int offset = this.vBuffer.limit();
            int vsize = this.getVertexSize();

            if (offset + vsize > this.vBuffer.capacity())
            {
                ByteBuffer dataIdx = this.vBuffer;
                this.vBuffer = ByteBuffer.allocateDirect(dataIdx.capacity() * 2).order(ByteOrder.nativeOrder());
                dataIdx.rewind();
                this.vBuffer.put(dataIdx);
            }

            this.vBuffer.limit(offset + vsize);
            this.vBuffer.position(offset);
            byte argIdx = 0;
            this.vBuffer.putFloat(data[argIdx][0]);
            this.vBuffer.putFloat(data[argIdx][1]);
            this.vBuffer.putFloat(data[argIdx][2]);
            int i = argIdx + 1;

            if (this.hasNormal)
            {
                this.vBuffer.putFloat(data[i][0]);
                this.vBuffer.putFloat(data[i][1]);
                this.vBuffer.putFloat(data[i][2]);
                ++i;
            }

            if (this.hasColor)
            {
                int r = (int)((double)(data[i][0] * 255.0F) + 0.5D);

                if (r > 255)
                {
                    r = 255;
                }
                else if (r < 0)
                {
                    r = 0;
                }

                int g = (int)((double)(data[i][1] * 255.0F) + 0.5D);

                if (g > 255)
                {
                    g = 255;
                }
                else if (g < 0)
                {
                    g = 0;
                }

                int b = (int)((double)(data[i][2] * 255.0F) + 0.5D);

                if (b > 255)
                {
                    b = 255;
                }
                else if (b < 0)
                {
                    b = 0;
                }

                int a = 255;

                if (data[i].length > 3)
                {
                    a = (int)((double)(data[i][3] * 255.0F) + 0.5D);

                    if (a > 255)
                    {
                        a = 255;
                    }
                    else if (a < 0)
                    {
                        a = 0;
                    }
                }

                if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN)
                {
                    this.vBuffer.putInt(a << 24 | b << 16 | g << 8 | r);
                }
                else
                {
                    this.vBuffer.putInt(r << 24 | g << 16 | b << 8 | a);
                }

                ++i;
            }

            if (this.texture >= 0)
            {
                this.vBuffer.putFloat(data[i][0]);
                this.vBuffer.putFloat(data[i][1]);
                ++i;
            }

            return offset / vsize;
        }

        public boolean canCopyFrom(VertexBuffer sourceBuffer)
        {
            return sourceBuffer == null ? false : (this.hasNormal && !sourceBuffer.hasNormal ? false : (this.hasColor && !sourceBuffer.hasColor ? false : this.texture < 0 || this.texture == sourceBuffer.texture));
        }

        public int copyVertex(VertexBuffer sourceBuffer, int sourceIndex)
        {
            if (!this.canCopyFrom(sourceBuffer))
            {
                return -1;
            }
            else if (sourceIndex >= 0 && sourceIndex <= sourceBuffer.getVertexCount())
            {
                byte[] tmp = new byte[16];
                sourceBuffer.vBuffer.position(sourceIndex * sourceBuffer.getVertexSize());
                int offset = this.vBuffer.limit();
                int vsize = this.getVertexSize();

                if (offset + vsize > this.vBuffer.capacity())
                {
                    ByteBuffer oldBuffer = this.vBuffer;
                    this.vBuffer = ByteBuffer.allocateDirect(oldBuffer.capacity() * 2).order(ByteOrder.nativeOrder());
                    oldBuffer.rewind();
                    this.vBuffer.put(oldBuffer);
                }

                this.vBuffer.limit(offset + vsize);
                this.vBuffer.position(offset);
                sourceBuffer.vBuffer.get(tmp, 0, 12);
                this.vBuffer.put(tmp, 0, 12);

                if (sourceBuffer.hasNormal)
                {
                    sourceBuffer.vBuffer.get(tmp, 0, 12);
                }

                if (this.hasNormal)
                {
                    this.vBuffer.put(tmp, 0, 12);
                }

                if (sourceBuffer.hasColor)
                {
                    sourceBuffer.vBuffer.get(tmp, 0, 4);
                }

                if (this.hasColor)
                {
                    this.vBuffer.put(tmp, 0, 4);
                }

                if (sourceBuffer.texture >= 0)
                {
                    sourceBuffer.vBuffer.get(tmp, 0, 8);
                }

                if (this.texture >= 0)
                {
                    this.vBuffer.put(tmp, 0, 8);
                }

                return offset / vsize;
            }
            else
            {
                return -1;
            }
        }

        public int getIndexCount()
        {
            return this.xBuffer.limit();
        }

        public void addIndex(int index)
        {
            int offset = this.xBuffer.limit();

            if (offset == this.xBuffer.capacity())
            {
                IntBuffer oldBuffer = this.xBuffer;
                this.xBuffer = ByteBuffer.allocateDirect(oldBuffer.capacity() * 8).order(ByteOrder.nativeOrder()).asIntBuffer();
                oldBuffer.rewind();
                this.xBuffer.put(oldBuffer);
            }

            this.xBuffer.limit(offset + 1);
            this.xBuffer.position(offset);
            this.xBuffer.put(index);
        }

        public void drawBuffer()
        {
            int vCount = this.getVertexCount();
            int vsize = this.getVertexSize();
            int xCount = this.getIndexCount();

            if (this.renderMode >= 0 && vCount != 0 && xCount != 0)
            {
                byte offset = 0;
                this.vBuffer.position(offset);
                GL11.glVertexPointer(3, GL11.GL_FLOAT, vsize, this.vBuffer.asFloatBuffer());
                GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
                int offset1 = offset + 12;

                if (this.hasNormal)
                {
                    this.vBuffer.position(offset1);
                    GL11.glNormalPointer(GL11.GL_FLOAT, vsize, this.vBuffer.asFloatBuffer());
                    GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
                    offset1 += 12;
                }

                if (this.hasColor)
                {
                    this.vBuffer.position(offset1);
                    GL11.glColorPointer(4, GL11.GL_BYTE, vsize, this.vBuffer);
                    GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
                    offset1 += 4;
                }

                if (this.texture >= 0)
                {
                    this.vBuffer.position(offset1);
                    GL11.glTexCoordPointer(2, GL11.GL_FLOAT, vsize, this.vBuffer.asFloatBuffer());
                    GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
                    offset1 += 8;
                }

                if (this.texture >= 0 && this.texture != GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D))
                {
                    GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.texture);
                }

                this.xBuffer.rewind();
                GL11.glDrawElements(this.renderMode, this.xBuffer);
                GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);

                if (this.hasNormal)
                {
                    GL11.glDisableClientState(GL11.GL_NORMAL_ARRAY);
                }

                if (this.hasColor)
                {
                    GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
                }

                if (this.texture >= 0)
                {
                    GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
                }
            }
        }
    }

}


<|start_filename|>src/main/java/CustomOreGen/Util/ConsoleCommand.java<|end_filename|>
package CustomOreGen.Util;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import CustomOreGen.CustomPacketPayload;
import CustomOreGen.CustomPacketPayload.PayloadType;
import CustomOreGen.Config.ConfigParser;
import CustomOreGen.Server.ServerState;
//import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
//import net.minecraft.command.ICommandSender;
//import net.minecraft.command.SyntaxErrorException;
import net.minecraft.entity.Entity;
//import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
//import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.World;
//import net.minecraft.world.WorldServer;

public class ConsoleCommand //extends CommandBase
{
	/*private final Object _obj;
    private final Method _method;

    public static void sendText(ICommandSender recipient, String text)
    {
        if (text != null && recipient != null)
        {
        	for (String line : text.split("\n")) {
        		if (recipient instanceof EntityPlayerMP)
        		{
        			(new CustomPacketPayload(PayloadType.CommandResponse, line)).sendToClient((EntityPlayerMP)recipient);
        		}
	            else
	            {
	            	recipient.sendMessage(new TextComponentString(line));
	            }
        	}
        }
    }

    public static WorldServer getSenderWorldServer(ICommandSender sender)
    {
        World entityWorld = null;

        if (sender instanceof Entity)
        {
            entityWorld = ((Entity)sender).world;
        }
        else
        {
            if (!(sender instanceof TileEntity))
            {
                return null;
            }

            entityWorld = ((TileEntity)sender).getWorld();
        }

        if (entityWorld == null)
        {
            return null;
        }
        else
        {
        	int dim = entityWorld.provider.getDimension();
            return sender.getServer().getWorld(dim);
        }
    }

    public ConsoleCommand(Object obj, Method method)
    {
        if ((method.getModifiers() & 8) == 0 && obj == null)
        {
            throw new RuntimeException("Method \'" + method.getName() + "\' for class " + method.getDeclaringClass().getSimpleName() + " requires an object instance");
        }
        else
        {
            this._obj = obj;
            this._method = method;
        }
    }

    public ConsoleCommand(Method method)
    {
        this((Object)null, method);
    }

    @Override
    public String getName()
    {
        CommandDelegate cmdDef = (CommandDelegate)this._method.getAnnotation(CommandDelegate.class);
        return cmdDef != null && cmdDef.names() != null && cmdDef.names().length > 0 ? cmdDef.names()[0] : this._method.getName();
    }

    @Override
    public List<String> getAliases()
    {
        CommandDelegate cmdDef = (CommandDelegate)this._method.getAnnotation(CommandDelegate.class);
        return cmdDef != null && cmdDef.names() != null && cmdDef.names().length > 1 ? Arrays.asList(Arrays.copyOfRange(cmdDef.names(), 1, cmdDef.names().length)) : Collections.<String>emptyList();
    }

    @Override
    public String getUsage(ICommandSender sender)
    {
        return this.getCommandHelp(sender, false);
    }

    public String getCommandHelp(ICommandSender sender, boolean verbose)
    {
        StringBuilder out = new StringBuilder("/" + this.getName());
        Class<?>[] ptypes = this._method.getParameterTypes();
        Annotation[][] pantns = this._method.getParameterAnnotations();

        for (int cmdDef = 0; cmdDef < ptypes.length; ++cmdDef)
        {
            Class<?> clazz = ptypes[cmdDef];
            String name = "arg" + cmdDef + ":" + clazz.getSimpleName();
            boolean required = true;

            if (pantns != null && cmdDef < pantns.length && pantns[cmdDef] != null)
            {
                Annotation[] senderWorld = pantns[cmdDef];   
                for (Annotation annot : senderWorld) {
                	if (annot instanceof ArgName)
                    {
                        name = ((ArgName)annot).name();
                    }
                    else if (annot instanceof ArgOptional)
                    {
                        required = false;
                    }
                }
            }

            if (!clazz.isAssignableFrom(ICommandSender.class))
            {
                if (clazz.isAssignableFrom(WorldServer.class))
                {
                    if (getSenderWorldServer(sender) != null)
                    	continue;

                    clazz = Integer.class;
                }
                else if (clazz.isArray() && cmdDef == ptypes.length - 1 && this._method.isVarArgs())
                {
                    name = " ... ";
                    required = false;
                }

                out.append(' ');

                if (required)
                {
                    out.append('<');
                }
                else
                {
                    out.append('[');
                }

                out.append(name);

                if (required)
                {
                    out.append('>');
                }
                else
                {
                    out.append(']');
                }
            }
        }

        CommandDelegate delegate = (CommandDelegate)this._method.getAnnotation(CommandDelegate.class);

        if (verbose && delegate != null && delegate.desc() != null && !delegate.desc().isEmpty())
        {
            out.append("\n  ");
            out.append(delegate.desc());
        }

        return out.toString();
    }

    @Override
    public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException {
        Class<?>[] ptypes = this._method.getParameterTypes();
        Annotation[][] pantns = this._method.getParameterAnnotations();
        Object[] pvalues = new Object[ptypes.length];

        try
        {
            int ex = 0;

            for (int pidx = 0; pidx < ptypes.length; ++pidx)
            {
                Class<?> clazz = ptypes[pidx];
                String name = "arg" + pidx + ":" + clazz.getSimpleName();
                String defValue = null;
                boolean required = true;

                if (pantns != null && pidx < pantns.length && pantns[pidx] != null)
                {
                    for (Annotation annot : pantns[pidx]) {
                    	if (annot instanceof ArgName)
                        {
                            name = ((ArgName)annot).name();
                        }
                        else if (annot instanceof ArgOptional)
                        {
                            required = false;
                            defValue = ((ArgOptional)annot).defValue();

                            if (defValue.isEmpty())
                            {
                                defValue = null;
                            }
                        }

                    }
                }

                if (clazz.isAssignableFrom(ICommandSender.class))
                {
                    pvalues[pidx] = sender;
                }
                else if (clazz.isAssignableFrom(WorldServer.class))
                {
                    pvalues[pidx] = getSenderWorldServer(sender);

                    if (pvalues[pidx] == null)
                    {
                    	Integer dimId = ConfigParser.parseString(Integer.class, ex < args.length ? args[ex++] : defValue);
                        pvalues[pidx] = dimId == null ? null : sender.getServer().getWorld(dimId.intValue());

                        if (pvalues[pidx] == null && required)
                        {
                            throw new SyntaxErrorException("Missing or invalid dimension ID for required argument \'" + name + "\'", new Object[0]);
                        }
                    }
                }
                else if (clazz.isArray() && pidx == ptypes.length - 1 && this._method.isVarArgs())
                {
                    pvalues[pidx] = Array.newInstance(clazz.getComponentType(), args.length - ex);

                    for (int i = 0; i < Array.getLength(pvalues[pidx]); ++i)
                    {
                        Object pvalue = ConfigParser.parseString(clazz.getComponentType(), args[ex++]);
                        Array.set(pvalues[pidx], i, pvalue);
                    }
                }
                else
                {
                    pvalues[pidx] = ConfigParser.parseString(clazz, ex < args.length ? args[ex++] : defValue);

                    if (pvalues[pidx] == null && required)
                    {
                        throw new SyntaxErrorException("Missing required argument \'" + name + "\'", new Object[0]);
                    }
                }
            }

            if (ex < args.length)
            {
                throw new SyntaxErrorException("Too many arguments", new Object[0]);
            }
        }
        catch (IllegalArgumentException var18)
        {
            throw new SyntaxErrorException(var18.getMessage(), new Object[0]);
        }

        try
        {
            Object result = this._method.invoke(this._obj, pvalues);

            if (result != null)
            {
                sendText(sender, result.toString());
            }
        }
        catch (InvocationTargetException var16)
        {
            throw new CommandException("Error: " + var16.getCause().getMessage(), new Object[0]);
        }
        catch (Exception var17)
        {
            throw new CommandException("Unkown Error: " + var17.getMessage(), new Object[0]);
        }
    }

	@Override
	public boolean checkPermission(MinecraftServer server, ICommandSender sender) {
		CommandDelegate cmdDef = (CommandDelegate)this._method.getAnnotation(CommandDelegate.class);

        if (cmdDef == null || cmdDef.isDebugging())
        {
            WorldServer senderWorld = getSenderWorldServer(sender);

            if (senderWorld != null)
            {
                ServerState.checkIfServerChanged(server, senderWorld.getWorldInfo());

                if (!ServerState.getWorldConfig(senderWorld).debuggingMode)
                {
                    return false;
                }
            }
        }

        return super.checkPermission(server, sender);
    }

    @Override
    public int getRequiredPermissionLevel()
    {
        CommandDelegate cmdDef = (CommandDelegate)this._method.getAnnotation(CommandDelegate.class);
        return cmdDef != null && cmdDef.isCheat() ? 2 : 0;
    }*/

}


<|start_filename|>src/main/java/CustomOreGen/Server/ConsoleCommands.java<|end_filename|>
package CustomOreGen.Server;

import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;

import CustomOreGen.CustomOreGenBase;
import CustomOreGen.CustomPacketPayload;
import CustomOreGen.CustomPacketPayload.PayloadType;
import CustomOreGen.CustomPacketPayloadHandler;
import CustomOreGen.Util.BiomeDescriptor;
import CustomOreGen.Util.BlockDescriptor;
import CustomOreGen.Util.PDist;
import CustomOreGen.Util.PDist.Type;
import net.minecraft.command.CommandSource;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.JsonToNBT;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.server.ServerWorld;

public class ConsoleCommands
{
    @Retention(RetentionPolicy.RUNTIME)
    @Target( {ElementType.PARAMETER})
    public @interface ArgName
    {
    	String name() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target( {ElementType.PARAMETER})
    public @interface ArgOptional
    {
    	String defValue() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target( {ElementType.METHOD})
    public @interface CommandDelegate
    {
    	String[] names() default {};

    	boolean isCheat() default true;

    	boolean isDebugging() default true;

    	String desc() default "";
    }
    
    public static void register(CommandDispatcher<CommandSource> dispatcher) {
        for (Method method : ConsoleCommands.class.getMethods()) {
        	if (method.getAnnotation(CommandDelegate.class) != null)
            {
                // TODO: process annotations and call builder methods on dispatcher.
            }
        }
    }
    
    private static void resetClientGeometryCache()
    {
        CustomPacketPayloadHandler.sendToAllClients(new CustomPacketPayload(PayloadType.DebuggingGeometryReset, (Serializable)null));
    }

    private static void buildFieldValue(StringBuilder msg, String indent, String name, String desc, Object value)
    {
        msg.append('\n');
        msg.append(indent);
        msg.append(name);
        msg.append(" = ");

        if (value != null)
        {
            msg.append('[');
            msg.append(value.getClass().getSimpleName());
            msg.append("] ");
            msg.append(value);

            if (desc != null)
            {
            	if (value instanceof BlockDescriptor)
                {
                	for (String line : ((BlockDescriptor)value).toDetailedString()) {
                		msg.append('\n');
                        msg.append(indent);
                        msg.append("  ");
                        msg.append(line);
                    }
                }
                else if (value instanceof BiomeDescriptor)
                {
                	for (String line : ((BiomeDescriptor)value).toDetailedString()) {
                        msg.append('\n');
                        msg.append(indent);
                        msg.append("  ");
                        msg.append(line);
                    }
                }
            }
        }
        else
        {
            msg.append("[null]");
        }

        if (desc != null)
        {
            msg.append('\n');
            msg.append(indent);
            msg.append("  ");
            msg.append("\u00a77");
            msg.append(desc);
            msg.append("\u00a7r");
        }
    }

    @CommandDelegate(
            desc = "Dumps a summary of a distribution or a specific distribution setting to the console."
    )
    public String cogInfo(
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) @ArgOptional String distribution,
            @ArgName(
                    name = "setting"
            ) @ArgOptional String setting,
            @ArgName(
                    name = "detail"
            )
            @ArgOptional(
                    defValue = "false"
            ) boolean detail)
    {
        StringBuilder msg = new StringBuilder();
        WorldConfig config = ServerState.getWorldConfig(world);
        Collection<IOreDistribution> allDists = config.getOreDistributions();
        Collection<IOreDistribution> dists = distribution != null && !distribution.isEmpty() ? config.getOreDistributions(distribution) : null;
        msg.append(CustomOreGenBase.getDisplayString() + " (");

        if (config.world != null)
        {
            msg.append("Dim " + config.world.getDimension() + ", ");
        }

        if (dists != null)
        {
            msg.append(dists.size() + "/");
        }

        msg.append(allDists.size() + " distributions");
        msg.append(')');

        if (dists != null && dists.size() > 0)
        {
            msg.append(':');
            Pattern pattern = setting != null && !setting.isEmpty() ? Pattern.compile(setting, 2) : null;
            
            for (IOreDistribution dist : dists) {
            	msg.append("\n " + dist);

                if (pattern != null)
                {
                    Map<String,String> descriptions = dist.getDistributionSettingDescriptions();
                    LinkedHashMap<String,Object> values = new LinkedHashMap<String, Object>();
                    
                    for (Entry<String,String> entry : descriptions.entrySet()) {
                    	if (pattern.matcher(entry.getKey()).matches())
                        {
                            Object value = dist.getDistributionSetting(entry.getKey());
                            values.put(entry.getKey(), value);
                        }
                    }
                    
                    msg.append(" (" + values.size() + "/" + descriptions.size() + " settings)");

                    if (values.size() > 0)
                    {
                        msg.append(':');
                    }

                    for (Entry<String,Object> entry : values.entrySet()) {
                    	buildFieldValue(msg, "  ", (String)entry.getKey(), detail ? (String)descriptions.get(entry.getKey()) : null, entry.getValue());
                    }
                }
            }
        }

        CustomOreGenBase.log.info(msg.toString());
        return msg.toString();
    }

    @CommandDelegate(
            desc = "Sets the global wireframe rendering mode.  Omit mode to cycle through modes."
    )
    public void cogWireframeMode(CommandSource sender,
            @ArgName(
                    name = "None|Polygon|Wireframe|WireframeOverlay"
            ) @ArgOptional String renderMode)
    {
        try {
			CustomPacketPayloadHandler.sendTo(new CustomPacketPayload(PayloadType.DebuggingGeometryRenderMode, renderMode), sender.asPlayer());
		} catch (CommandSyntaxException e) {
			throw new IllegalArgumentException("/cogWireframeMode is a client-side command and may only be used by a player.");
		}
    }

    @CommandDelegate(
            desc = "Clears cached structure information.  Omit distribution name to clear all distributions."
    )
    public String cogClear(
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            )
            @ArgOptional(
                    defValue = ".*"
            ) String distribution)
    {
        int count = 0;
        
        for (IOreDistribution dist : ServerState.getWorldConfig(world).getOreDistributions(distribution)) {
        	++count;
            dist.clear();
            dist.validate();
        }

        resetClientGeometryCache();
        return "Cleared " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Repopulates chunks in the specified range around the player.  Omit distribution name to repopulate all distributions."
    )
    public String cogPopulate(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "chunkRange"
            ) int chunkRange,
            @ArgName(
                    name = "distribution"
            )
            @ArgOptional(
                    defValue = ".*"
            ) String distribution,
            @ArgName(
                    name = "centerX"
            ) @ArgOptional Integer centerX,
            @ArgName(
                    name = "centerZ"
            ) @ArgOptional Integer centerZ)
    {
        WorldConfig cfg = ServerState.getWorldConfig(world);
        Collection<IOreDistribution> list = cfg.getOreDistributions(distribution);
        BlockPos senderPos;
        try {
			sender.assertIsEntity();
		} catch (CommandSyntaxException e) {
			sendError(sender, e);
		}
		senderPos = sender.getEntity().getPosition();
		int cX = centerX == null ? senderPos.getX() : centerX.intValue();
        int cZ = centerZ == null ? senderPos.getZ() : centerZ.intValue();

        for (int chunkX = (cX >> 4) - chunkRange; chunkX <= (cX >> 4) + chunkRange; ++chunkX)
        {
            for (int chunkZ = (cZ >> 4) - chunkRange; chunkZ <= (cZ >> 4) + chunkRange; ++chunkZ)
            {
                cfg.world.getChunk(chunkX, chunkZ);
                ServerState.populateDistributions(list, cfg.world, chunkX, chunkZ);
            }
        }

        return "Populated " + list.size() + " distributions in " + (2 * chunkRange + 1) * (2 * chunkRange + 1) + " chunk(s) around (" + cX + ",0," + cZ + ")";
    }

    @CommandDelegate(
            desc = "Sets the parent distribution.  Omit parent name to clear parent distribution."
    )
    public String cogParent(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "parent"
            ) @ArgOptional String parent)
    {
        WorldConfig cfg = ServerState.getWorldConfig(world);
        IOreDistribution parentDist = null;

        if (parent != null)
        {
            Collection<IOreDistribution> count = cfg.getOreDistributions(parent);

            if (count.isEmpty())
            {
                throw new IllegalArgumentException("Parent name \'" + parent + "\' does not match any distributions.");
            }

            if (count.size() > 1)
            {
                throw new IllegalArgumentException("Parent name \'" + parent + "\' is ambiguous (matches " + count.size() + " distributions).");
            }

            parentDist = (IOreDistribution)count.iterator().next();
        }

        int count = 0;
        
        for (IOreDistribution dist : cfg.getOreDistributions(distribution)) {
        	try
            {
                dist.setDistributionSetting(IOreDistribution.StandardSettings.Parent.name(), parentDist);
                ++count;
                dist.clear();
                dist.validate();
            }
            catch (Exception var11)
            {
                sender.sendErrorMessage(new StringTextComponent("\u00a7c" + var11.getMessage()));
            }
        }

        resetClientGeometryCache();
        return "Changed parent for " + count + " distributions";
    }

    private static int changeBiomeDescriptor(String settingName, CommandSource sender, ServerWorld world, String distribution, String descriptor, float weight, boolean clear)
    {
        int count = 0;
        
        for (IOreDistribution dist : ServerState.getWorldConfig(world).getOreDistributions(distribution)) {
        	try
            {
                Object ex = dist.getDistributionSetting(settingName);

                if (ex == null)
                {
                    throw new IllegalArgumentException("Distribution \'" + dist + "\' does not support descriptor " + settingName + ".");
                }
                
                if (!(ex instanceof BiomeDescriptor))
                {
                    throw new IllegalArgumentException("Setting " + settingName + " on Distribution \'" + dist + "\' is not a biome descriptor.");
                }

                BiomeDescriptor desc = (BiomeDescriptor)ex;

                if (clear)
                {
                    desc.clear();
                }

                desc.add(descriptor, weight);
                ++count;

                dist.clear();
                dist.validate();
            } catch (Exception var12)
            {
                sendError(sender, var12);
            }
        }
        
        resetClientGeometryCache();
        return count;
    }
    
    private static int changeBlockDescriptor(String settingName, CommandSource sender, ServerWorld world, String distribution, String descriptor, float weight, boolean clear, boolean describesOre, boolean matchFirst, boolean isRegexp, String nbt)
    {
        int count = 0;
        
        for (IOreDistribution dist : ServerState.getWorldConfig(world).getOreDistributions(distribution)) {
        	try
            {
                Object ex = dist.getDistributionSetting(settingName);

                if (ex == null)
                {
                    throw new IllegalArgumentException("Distribution \'" + dist + "\' does not support descriptor " + settingName + ".");
                }

                if (!(ex instanceof BlockDescriptor))
                {
                    throw new IllegalArgumentException("Setting " + settingName + " on Distribution \'" + dist + "\' is not a block descriptor.");
                }
                
                if (ex instanceof BlockDescriptor)
                {
                    BlockDescriptor desc = (BlockDescriptor)ex;

                    if (clear)
                    {
                        desc.clear();
                    }

                    CompoundNBT compoundNBT = JsonToNBT.getTagFromJson(nbt);
                    desc.add(descriptor, weight, describesOre, isRegexp, matchFirst, compoundNBT);
                    ++count;
                }
                
                dist.clear();
                dist.validate();
            }
            catch (Exception var12)
            {
                sendError(sender, var12);
            }
        }

        resetClientGeometryCache();
        return count;
    }

    @CommandDelegate(
            desc = "Adds an ore block."
    )
    public String cogAddOreBlock(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "block"
            ) String block,
            @ArgName(
                    name = "weight"
            )
            @ArgOptional(
                    defValue = "1"
            ) float weight,
            @ArgName(
                    name = "matchFirstOre"
            )
            @ArgOptional(
                    defValue = "false"
            ) boolean matchFirstOre,
            @ArgName(
            		name = "nbt"
            )
    		@ArgOptional String nbt)
    {
        String setting = IOreDistribution.StandardSettings.OreBlock.name();
        int count = changeBlockDescriptor(setting, sender, world, distribution, block, weight, false, matchFirstOre, matchFirstOre, false, nbt);
        return "Added ore block for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Sets the ore block (clearing any previous ore blocks)."
    )
    public String cogSetOreBlock(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "block"
            ) String block,
            @ArgName(
                    name = "weight"
            )
            @ArgOptional(
                    defValue = "1"
            ) float weight,
            @ArgName(
                    name = "matchFirstOre"
            )
            @ArgOptional(
                    defValue = "false"
            ) boolean matchFirstOre,
            @ArgName(
            		name = "nbt"
            )
    		@ArgOptional String nbt)
    {
        String setting = IOreDistribution.StandardSettings.OreBlock.name();
        int count = changeBlockDescriptor(setting, sender, world, distribution, block, weight, true, matchFirstOre, matchFirstOre, false, nbt);
        return "Set ore block for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Adds a replaceable block."
    )
    public String cogAddReplaceable(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "block"
            ) String block,
            @ArgName(
                    name = "weight"
            )
            @ArgOptional(
                    defValue = "1"
            ) float weight,
            @ArgName(
            		name = "replacesOre"
            ) @ArgOptional boolean replacesOre,
            @ArgName(
            		name = "isRegexp"
            ) @ArgOptional boolean isRegexp)
    {
        String setting = IOreDistribution.StandardSettings.Replaces.name();
        int count = changeBlockDescriptor(setting, sender, world, distribution, block, weight, false, replacesOre, false, isRegexp, null);
        return "Added replaceable block for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Sets the replaceable block (clearing any previous replaceable blocks)."
    )
    public String cogSetReplaceable(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "block"
            ) String block,
            @ArgName(
                    name = "weight"
            )
            @ArgOptional(
                    defValue = "1"
            ) float weight,
            @ArgName(
            		name = "replacesOre"
            ) @ArgOptional boolean replacesOre,
            @ArgName(
            		name = "isRegexp"
            ) @ArgOptional boolean isRegexp)
    {
        String setting = IOreDistribution.StandardSettings.Replaces.name();
        int count = changeBlockDescriptor(setting, sender, world, distribution, block, weight, true, replacesOre, false, isRegexp, null);
        return "Set replaceable block for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Adds a target biome."
    )
    public String cogAddBiome(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "biome"
            ) String biome,
            @ArgName(
                    name = "weight"
            )
            @ArgOptional(
                    defValue = "1"
            ) float weight)
    {
        String setting = IOreDistribution.StandardSettings.TargetBiome.name();
        int count = changeBiomeDescriptor(setting, sender, world, distribution, biome, weight, false);
        return "Added biome for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Sets the target biome (clearing any previous biomes)."
    )
    public String cogSetBiome(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "biome"
            ) String biome,
            @ArgName(
                    name = "weight"
            )
            @ArgOptional(
                    defValue = "1"
            ) float weight)
    {
        String setting = IOreDistribution.StandardSettings.TargetBiome.name();
        int count = changeBiomeDescriptor(setting, sender, world, distribution, biome, weight, true);
        return "Set biome for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Sets a distribution setting.  Setting names are the same as the <Setting> names in the config file.  Range and Type are optional (default to 0 and \'uniform\', respectively)."
    )
    public String cogSetting(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "setting"
            ) String setting,
            @ArgName(
                    name = "average"
            ) float average,
            @ArgName(
                    name = "range"
            )
            @ArgOptional(
                    defValue = "0"
            ) float range,
            @ArgName(
                    name = "type"
            )
            @ArgOptional(
                    defValue = "uniform"
            ) Type type)
    {
        int count = 0;
        
        for (IOreDistribution dist : ServerState.getWorldConfig(world).getOreDistributions(distribution))
        {
            try
            {
                dist.setDistributionSetting(setting, new PDist(average, range, type));
                ++count;
                dist.clear();
                dist.validate();
            }
            catch (Exception var12)
            {
                sendError(sender, var12);
            }
        }

        resetClientGeometryCache();
        return "Changed \'" + setting + "\' for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Sets a simple numeric/boolean/string/enum setting.  Setting names are the same as the corresponding attributes in the config file."
    )
    public String cogSettingEx(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "distribution"
            ) String distribution,
            @ArgName(
                    name = "setting"
            ) String setting,
            @ArgName(
                    name = "value"
            ) String value)
    {
        int count = 0;

        for (IOreDistribution dist : ServerState.getWorldConfig(world).getOreDistributions(distribution))
        {
        	try
            {
                dist.setDistributionSetting(setting, value);
                ++count;
                dist.clear();
                dist.validate();
            }
            catch (Exception var10)
            {
                sendError(sender, var10.getMessage());
            }
        }

        resetClientGeometryCache();
        return "Changed \'" + setting + "\' for " + count + " distributions";
    }

    @CommandDelegate(
            desc = "Dumps a summary of an Option to the console."
    )
    public String cogOptionInfo(
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "option"
            ) @ArgOptional String option,
            @ArgName(
                    name = "detail"
            )
            @ArgOptional(
                    defValue = "false"
            ) boolean detail)
    {
        StringBuilder msg = new StringBuilder();
        WorldConfig config = ServerState.getWorldConfig(world);
        Collection<ConfigOption> allOptions = config.getConfigOptions();
        Collection<ConfigOption> options = option != null && !option.isEmpty() ? config.getConfigOptions(option) : null;
        msg.append(CustomOreGenBase.getDisplayString() + " (");

        if (config.world != null)
        {
            msg.append("Dim " + config.world.getDimension().getType().getRegistryName() + ", ");
        }

        if (options != null)
        {
            msg.append(options.size() + "/");
        }

        msg.append(allOptions.size() + " options");
        msg.append(')');

        if (options != null && options.size() > 0)
        {
            msg.append(':');
            
            for (ConfigOption opt : options) {
            	buildFieldValue(msg, "  ", opt.getName(), detail ? opt.getDescription() : null, opt.getValue());
            }
        }

        CustomOreGenBase.log.info(msg.toString());
        return msg.toString();
    }

    @CommandDelegate(
            desc = "Set an option value for the current dimension.  The change lasts until the world configuration is reloaded."
    )
    public String cogOption(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "option"
            ) String option,
            @ArgName(
                    name = "value"
            ) String value)
    {
        WorldConfig cfg = ServerState.getWorldConfig(world);
        Collection<ConfigOption> options = cfg.getConfigOptions(option);
        int count = 0;
        Iterator<ConfigOption> path = options.iterator();

        while (path.hasNext())
        {
            ConfigOption opt = path.next();

            if (opt.setValue(value))
            {
                ++count;
            }
            else
            {
                sendError(sender, "Invalid value \'" + value + "\' for Option \'" + opt.getName() + "\'");
            }
        }

        WorldConfig.loadedOptionOverrides[2] = options;
        ServerState.clearWorldConfig(world);
        cfg = ServerState.getWorldConfig(world);
        String dim = cfg.dimensionDir.toString();
        WorldConfig.loadedOptionOverrides[2] = null;
        resetClientGeometryCache();
        return "Changed " + count + " options for " + dim;
    }

    @CommandDelegate(
            isDebugging = false,
            desc = "Enabled or disable debugging mode for the current dimension."
    )
    public String cogEnableDebugging(CommandSource sender,
            @ArgName(
                    name = "dimension"
            ) ServerWorld world,
            @ArgName(
                    name = "enable"
            )
            @ArgOptional(
                    defValue = "true"
            ) boolean enabled)
    {
        return this.cogOption(sender, world, "debugMode", Boolean.toString(enabled));
    }

    @CommandDelegate(
            desc = "Reloads the world configuration from disk.  This will reset any changes made via console commands."
    )
    public String cogLoadConfig(
            @ArgName(
                    name = "dimension"
            ) ServerWorld world)
    {
        ServerState.clearWorldConfig(world);
        String path = ServerState.getWorldConfig(world).dimensionDir.toString();
        resetClientGeometryCache();
        return "Reloaded config data for " + path;
    }
    
    private static void sendError(CommandSource source, Exception exception) {
    	sendError(source, exception.getMessage());
    }

	private static void sendError(CommandSource source, String message) {
		source.sendErrorMessage(new StringTextComponent("\u00a7c" + message));
	}
}


<|start_filename|>src/main/java/CustomOreGen/Util/BlockArrangement.java<|end_filename|>
package CustomOreGen.Util;

import java.util.Random;

import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

public class BlockArrangement {
    private BlockDescriptor center, above, below, beside;
    private TouchingDescriptorList touching;

    public BlockArrangement(BlockDescriptor center, BlockDescriptor above, BlockDescriptor below,
            BlockDescriptor beside, TouchingDescriptorList touching) {
        super();
        this.center = center;
        this.above = above;
        this.below = below;
        this.beside = beside;
        this.touching = touching;
    }

    public boolean matchesAt(World world, Random rand, BlockPos pos) {
        return 
            this.descriptorMatchesAt(center, world, rand, pos) &&
            this.descriptorMatchesAt(above, world, rand, pos.up()) &&
            this.descriptorMatchesAt(below, world, rand, pos.down()) && 
            
            (this.descriptorMatchesAt(beside, world, rand, pos.north()) ||
            this.descriptorMatchesAt(beside, world, rand, pos.east()) ||
            this.descriptorMatchesAt(beside, world, rand, pos.south()) ||
            this.descriptorMatchesAt(beside, world, rand, pos.west())) &&
            
            this.touchesAt(world, rand, pos);
    }

    private boolean descriptorMatchesAt(BlockDescriptor descriptor, World world, Random rand, BlockPos pos) {
        if (descriptor.isEmpty()) {
            return true;
        }
        BlockState blockState = world.getBlockState(pos);
        return descriptor.matchesBlock(blockState, rand);
    }

    private boolean touchesAt(World world, Random rand, BlockPos pos) {
        if (this.touching.size() <= 0)
            return true;

        // mandatory touching descriptors, all must pass
        for (TouchingDescriptor descriptor : this.touching) {
            if (descriptor.mandatory) {
                if (!touchesAt(descriptor, world, rand, pos)) {
                    // mandatory descriptor failed, can't place block
                    return false;
                }
            }
        }

        // optional touching descriptors, at least one must pass
        int numberOfOptionalDescriptors = 0;
        for (TouchingDescriptor descriptor : this.touching) {
            if (!descriptor.mandatory) {
                numberOfOptionalDescriptors++;

                if (touchesAt(descriptor, world, rand, pos)) {
                    // one passed
                    return true;
                }
            }
        }

        if (numberOfOptionalDescriptors <= 0) {
            // all mandatory passed and there are no optional
            return true;
        }

        // no optionals passed or there are no touching rules for this distribution
        return false;
    }

    private boolean touchesAt(TouchingDescriptor descriptor, World world, Random rand, BlockPos pos) {
        int numberOfTouches = 0;

        // search each required block position
        for (BlockPos positionDelta : descriptor.deltaPositionMap) {
            if (descriptorMatchesAt(descriptor.blockDescriptor, world, rand, pos.add(positionDelta))) {
                numberOfTouches++;
            }
        }

        boolean returnValue = true;

        if (numberOfTouches < descriptor.minimumTouches || numberOfTouches > descriptor.maximumTouches)
            returnValue = false;

        if (descriptor.negate) {
            returnValue = !returnValue;
        }

        return returnValue;
    }

    public BlockDescriptor getCenter() {
        return center;
    }

    public BlockDescriptor getAbove() {
        return above;
    }

    public BlockDescriptor getBelow() {
        return below;
    }

    public BlockDescriptor getBeside() {
        return beside;
    }

    public TouchingDescriptorList getTouching() {
        return touching;
    }
}


<|start_filename|>src/main/java/CustomOreGen/Util/TileEntityHelper.java<|end_filename|>
package CustomOreGen.Util;

import java.util.Iterator;
import java.util.Set;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;

@SuppressWarnings("unchecked")
public class TileEntityHelper {

	public static void readFromPartialNBT(TileEntity te, CompoundNBT source) {
		CompoundNBT dest = new CompoundNBT();
		te.write(dest);
		mergeNbt(source, dest);
		te.read(dest);
	}
	
	public static void readFromPartialNBT(World world, int x, int y, int z, CompoundNBT source) {
		if (source != null) {
			BlockPos pos = new BlockPos(x, y, z);
			TileEntity te = world.getTileEntity(pos);
			if (te == null) {
				te = tryToCreateGTPrefixBlockTileEntity(world.getBlockState(pos).getBlock());
				if (te != null) {
					world.setTileEntity(pos, te);
				}
			}
			if (te != null) {
				TileEntityHelper.readFromPartialNBT(te, source);
				BlockState state = world.getBlockState(pos);
				world.notifyBlockUpdate(pos, state, state, 2);
			}
		}
	}
	
	static private Class<? extends Block> prefixBlockClass;
	static private Class<? extends TileEntity> prefixBlockTileEntityClass;
	
	static {
		try {
			prefixBlockClass = (Class<? extends Block>) Class.forName("gregapi.block.prefixblock.PrefixBlock");
			prefixBlockTileEntityClass = (Class<? extends TileEntity>) Class.forName("gregapi.block.prefixblock.PrefixBlockTileEntity");
		} catch (ClassNotFoundException e) {
		}
	}
	
	public static CompoundNBT tryToCreateGTPrefixBlockNBT(ItemStack ore) {
		Block block = ((BlockItem)ore.getItem()).getBlock();
		CompoundNBT nbt = null;
		if (isGTPrefixBlock(block)) {
			nbt = new CompoundNBT();
			nbt.putString("id", "gt.MetaBlockTileEntity");
		}
		return nbt;
	}
	
	private static boolean isGTPrefixBlock(Block block) {
		return prefixBlockClass != null && prefixBlockClass.isAssignableFrom(block.getClass());
	}
	
	private static TileEntity tryToCreateGTPrefixBlockTileEntity(Block block) {
		try {
			if (isGTPrefixBlock(block)) {
				return prefixBlockTileEntityClass.newInstance();
			}
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		return null;
	}

	private static void mergeNbt(CompoundNBT source, CompoundNBT dest) {
		Iterator<String> keys = ((Set<String>)source.keySet()).iterator(); 
		while(keys.hasNext()) {
			String key = keys.next();
			dest.put(key, source.getCompound(key));
		}
	}
	
}


<|start_filename|>src/main/java/CustomOreGen/ModEventBusSubscriber.java<|end_filename|>
package CustomOreGen;

import java.util.Random;

import javax.annotation.Nonnull;

import CustomOreGen.Server.ServerState;
import CustomOreGen.Server.WorldGenFeature;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.world.chunk.AbstractChunkProvider;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent;

@EventBusSubscriber(modid = CustomOreGenBase.MODID, bus = EventBusSubscriber.Bus.MOD)
public class ModEventBusSubscriber {
    /** FIXME: Used to be called by registering this as an IWorldGenerator. What now?<br>
     *        This does two things: it first generates the distribution structures, without actually populating them.
     *        It then populates this chunk or any of its neighboring chunks if they now have a complete neighborhood.
     *        <br><br>
     *        The generation step could happen through the new Feature system, where each structure is a Feature.
     *        However, the population step falls outside of the system. There could be a populating Feature that runs after the rest.
     *        Better would be a Forge event when a chunk changes status. Population could run after FEATURES completion.
     */
	@Deprecated
	@SuppressWarnings("rawtypes")
	public void generate(Random random, int chunkX, int chunkZ, World world, ChunkGenerator chunkGenerator, AbstractChunkProvider chunkProvider) {
		ServerState.checkIfServerChanged(world.getServer(), world.getWorldInfo());
        ServerState.onPopulateChunk(world, chunkX, chunkZ, random);
	}

    @SubscribeEvent
	public static void onFMLPreInit(FMLCommonSetupEvent event) {
    	CustomPacketPayloadHandler.register();
    }
    
    /**
     * This Feature attempts to replicate what {@link ModEventBusSubscriber#generate} did previously.
     */
    @SubscribeEvent
	public static void registerFeature(@Nonnull final RegistryEvent.Register<Feature<?>> event) {
    	event.getRegistry().register(new WorldGenFeature(NoFeatureConfig::deserialize).setRegistryName(new ResourceLocation(CustomOreGenBase.MODID, "cog_feature")));
	}
    
    @SubscribeEvent
	public static void onFMLPostInit(FMLLoadCompleteEvent event) {
    	CustomOreGenBase.onModPostLoad();
    }
}


<|start_filename|>src/main/java/CustomOreGen/Server/WorldGenFeature.java<|end_filename|>
package CustomOreGen.Server;

import java.util.Random;
import java.util.function.Function;

import com.mojang.datafixers.Dynamic;

import CustomOreGen.CustomOreGenBase;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraftforge.registries.ObjectHolder;

public class WorldGenFeature extends Feature<NoFeatureConfig> {

	@ObjectHolder(value = CustomOreGenBase.MODID+":cog_feature")
	public static final Feature<NoFeatureConfig> FEATURE = null;

	public WorldGenFeature(Function<Dynamic<?>, ? extends NoFeatureConfig> configFactoryIn) {
		super(configFactoryIn, false); //bool: do block updates or not
	}

	@Override
	public boolean place(IWorld worldIn, ChunkGenerator<? extends GenerationSettings> generator, Random rand, BlockPos pos, NoFeatureConfig config) {
        ServerState.checkIfServerChanged(worldIn.getWorld().getServer(), worldIn.getWorldInfo());
        ChunkPos p = new ChunkPos(pos);
        ServerState.onPopulateChunk(worldIn.getWorld(), p.x, p.z, worldIn.getRandom()); //TOOD: should use IWorld, not World
		return false;
	}
}


<|start_filename|>src/main/java/CustomOreGen/Util/CogOreGenEvent.java<|end_filename|>
package CustomOreGen.Util;

import java.util.Random;

import net.minecraft.world.chunk.IChunk;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.eventbus.api.Cancelable;

/**
 * CogOreGenEvent is fired just after a chunk is populated with ores by COG.<br>
 * This event is fired {TODO}<br>
 * <br>
 * This event is not {@link Cancelable}.<br>
 * <br>
 * This event does not have a result. {@link HasResult} <br>
 **/ 
public class CogOreGenEvent extends ChunkEvent {
    private final Random rand;
    
    public CogOreGenEvent(IChunk chunk, Random rand) {
    	super(chunk);
        this.rand = rand;
    }

    public Random getRand()
    {
        return rand;
    }
}


<|start_filename|>src/main/java/CustomOreGen/CustomPacketPayload.java<|end_filename|>
package CustomOreGen;

import java.io.Serializable;

import org.apache.commons.lang3.SerializationUtils;

import net.minecraft.network.PacketBuffer;

public class CustomPacketPayload
{
    public final PayloadType type;
    public final Serializable data;    
    
    public CustomPacketPayload(PayloadType type, Serializable data)
    {
        this.type = type;
        this.data = data;
    }

    public static void encode(CustomPacketPayload msg, PacketBuffer buf)
    {
        buf.writeByte((byte)msg.type.ordinal());
        buf.writeByteArray(SerializationUtils.serialize(msg.data));
    }
    
    public static CustomPacketPayload decode(PacketBuffer buf)
    {
    	PayloadType type1 = PayloadType.values()[buf.readByte()];
    	Serializable data1 = (Serializable)SerializationUtils.deserialize(buf.readByteArray());
    	return new CustomPacketPayload(type1, data1);
    }
    
    public enum PayloadType
    {
        DebuggingGeometryRequest,
        DebuggingGeometryReset,
        DebuggingGeometryData,
        DebuggingGeometryRenderMode,
        MystcraftSymbolData,
        CommandResponse;
    }
}


<|start_filename|>src/main/java/CustomOreGen/MystcraftSymbolData.java<|end_filename|>
package CustomOreGen;

import java.io.Serializable;

import net.minecraft.world.World;

public class MystcraftSymbolData implements Serializable
{
    public transient World world;
    public int dimensionID = 0;
    public String symbolName;
    public int count = 0;
    public String displayName = "";
    public float weight = 1.0F;
    public float instability = 0.0F;
    private static final long serialVersionUID = 1L;

    public MystcraftSymbolData() {}

    public MystcraftSymbolData(World world, String symbolName)
    {
        this.world = world;

        if (world != null)
        {
            this.dimensionID = world.dimension.getType().getId();
        }

        this.symbolName = this.displayName = symbolName;
    }
}
