public class ChestMaterial extends ForgeRegistryEntry<ChestMaterial> {

    public static final List<ChestMaterial> VALUES = Lists.newArrayList();
    public static final Map<String, ChestMaterial> KEYED_VALUES = Maps.newHashMap();

    public static final ChestMaterial WOOD = new ChestMaterial("wood", 1);
    public static final ChestMaterial COPPER = new ChestMaterial("copper", 1.666);
    public static final ChestMaterial IRON = new ChestMaterial("iron", 2);
    public static final ChestMaterial SILVER = new ChestMaterial("silver", 2.666);
    public static final ChestMaterial GOLD = new ChestMaterial("gold", 3);
    public static final ChestMaterial DIAMOND = new ChestMaterial("diamond", 4);
    public static final ChestMaterial OBSIDIAN = new ChestMaterial("obsidian", 4);

    private final String name;
    private final double inventoryMultiplier;
    private final int index;

    private ColossalChest blockCore;
    private Interface blockInterface;
    private ChestWall blockWall;
    private CubeDetector chestDetector = null;
    private ContainerType<ContainerColossalChest> container;

    public ChestMaterial(String name, double inventoryMultiplier) {
        this.name = name;
        this.inventoryMultiplier = inventoryMultiplier;
        this.index = ChestMaterial.VALUES.size();
        ChestMaterial.VALUES.add(this);
        ChestMaterial.KEYED_VALUES.put(getName(), this);
    }

    public static ChestMaterial valueOf(String materialString) {
        return KEYED_VALUES.get(materialString.toLowerCase());
    }

    public String getName() {
        return name;
    }

    public double getInventoryMultiplier() {
        return this.inventoryMultiplier;
    }

    public String getUnlocalizedName() {
        return "material." + Reference.MOD_ID + "." + getName();
    }

    public boolean isExplosionResistant() {
        return this == OBSIDIAN;
    }

    public int ordinal() {
        return this.index;
    }

    public ColossalChest getBlockCore() {
        return blockCore;
    }

    public void setBlockCore(ColossalChest blockCore) {
        if (this.blockCore != null) {
            throw new IllegalStateException("Tried registering multiple core blocks for " + this.getName());
        }
        this.blockCore = blockCore;
    }

    public Interface getBlockInterface() {
        return blockInterface;
    }

    public void setBlockInterface(Interface blockInterface) {
        if (this.blockInterface != null) {
            throw new IllegalStateException("Tried registering multiple core blocks for " + this.getName());
        }
        this.blockInterface = blockInterface;
    }

    public ChestWall getBlockWall() {
        return blockWall;
    }

    public void setBlockWall(ChestWall blockWall) {
        if (this.blockWall != null) {
            throw new IllegalStateException("Tried registering multiple core blocks for " + this.getName());
        }
        this.blockWall = blockWall;
    }

    public void setContainer(ContainerType<ContainerColossalChest> container) {
        if (this.container != null) {
            throw new IllegalStateException("Tried registering multiple containers for " + this.getName());
        }
        this.container = container;
    }

    public ContainerType<ContainerColossalChest> getContainer() {
        return container;
    }

    public CubeDetector getChestDetector() {
        if (chestDetector == null) {
            chestDetector = new HollowCubeDetector(
                    new AllowedBlock[]{
                            new AllowedBlock(getBlockWall()),
                            new AllowedBlock(getBlockCore()).addCountValidator(new ExactBlockCountValidator(1)),
                            new AllowedBlock(getBlockInterface())
                    },
                    Lists.newArrayList(getBlockCore(), getBlockWall(), getBlockInterface())
            )
                    .addSizeValidator(new MinimumSizeValidator(new Vector3i(1, 1, 1)))
                    .addSizeValidator(new CubeSizeValidator())
                    .addSizeValidator(new MaximumSizeValidator(TileColossalChest.getMaxSize()) {
                        @Override
                        public Vector3i getMaximumSize() {
                            return TileColossalChest.getMaxSize();
                        }
                    });
        }
        return chestDetector;
    }

} public class ChestWallConfig extends BlockConfig {

    public ChestWallConfig(ChestMaterial material) {
        super(
                ColossalChests._instance,
            "chest_wall_" + material.getName(),
                eConfig -> new ChestWall(Block.Properties.create(Material.ROCK)
                        .hardnessAndResistance(5.0F)
                        .sound(SoundType.WOOD)
                        .harvestLevel(0) // Wood tier
                        .notSolid(),
                        material),
                (eConfig, block) -> new ItemBlockMaterial(block, new Item.Properties()
                        .group(ColossalChests._instance.getDefaultItemGroup()), material)
        );
    }
    
} public class ColossalChestConfig extends BlockConfig {

    @ConfigurableProperty(namedId="colossal_chest", category = "machine", comment = "The maximum size a colossal chest can have.", isCommandable = true, configLocation = ModConfig.Type.SERVER)
    public static int maxSize = 20;

    @ConfigurableProperty(namedId="colossal_chest", category = "general", comment = "If the chest should visually open when someone uses it.", isCommandable = true, configLocation = ModConfig.Type.CLIENT)
    public static boolean chestAnimation = true;

    public ColossalChestConfig(ChestMaterial material) {
        super(
                ColossalChests._instance,
            "colossal_chest_" + material.getName(),
                eConfig -> new ColossalChest(Block.Properties.create(Material.ROCK)
                        .hardnessAndResistance(5.0F)
                        .sound(SoundType.WOOD)
                        .harvestLevel(0) // Wood tier
                        .notSolid(),
                        material),
                (eConfig, block) -> new ItemBlockMaterial(block, new Item.Properties()
                        .group(ColossalChests._instance.getDefaultItemGroup()), material)
        );
    }
    
} public class InterfaceConfig extends BlockConfig {

    public InterfaceConfig(ChestMaterial material) {
        super(
                ColossalChests._instance,
            "interface_" + material.getName(),
                eConfig -> new Interface(Block.Properties.create(Material.ROCK)
                        .hardnessAndResistance(5.0F)
                        .sound(SoundType.WOOD)
                        .harvestLevel(0) // Wood tier
                        .notSolid(),
                        material),
                (eConfig, block) -> new ItemBlockMaterial(block, new Item.Properties()
                        .group(ColossalChests._instance.getDefaultItemGroup()), material)
        );
    }
    
} public class UncolossalChestConfig extends BlockConfig {

    public UncolossalChestConfig() {
        super(
                ColossalChests._instance,
            "uncolossal_chest",
                eConfig -> new UncolossalChest(Block.Properties.create(Material.ROCK)
                        .hardnessAndResistance(5.0F)
                        .sound(SoundType.WOOD)
                        .harvestLevel(0)),
                getDefaultItemConstructor(ColossalChests._instance, (itemProperties) -> itemProperties.setISTER(() -> ItemStackTileEntityUncolossalChestRender::new))
        );
    }
    
} public class ContainerColossalChestConfig extends GuiConfig<ContainerColossalChest> {

    public ContainerColossalChestConfig() {
        super(ColossalChests._instance,
                "colossal_chest",
                eConfig -> new ContainerType<>((IContainerFactory<ContainerColossalChest>) ContainerColossalChest::new));
    }

    @OnlyIn(Dist.CLIENT)
    @Override
    public <U extends Screen & IHasContainer<ContainerColossalChest>> ScreenManager.IScreenFactory<ContainerColossalChest, U> getScreenFactory() {
        return new ScreenFactorySafe<>(ContainerScreenColossalChest::new);
    }

} public class ContainerUncolossalChestConfig extends GuiConfig<ContainerUncolossalChest> {

    public ContainerUncolossalChestConfig() {
        super(ColossalChests._instance,
                "uncolossal_chest",
                eConfig -> new ContainerType<>(ContainerUncolossalChest::new));
    }

    @OnlyIn(Dist.CLIENT)
    @Override
    public <U extends Screen & IHasContainer<ContainerUncolossalChest>> ScreenManager.IScreenFactory<ContainerUncolossalChest, U> getScreenFactory() {
        return new ScreenFactorySafe<>(ContainerScreenUncolossalChest::new);
    }

} public class ItemUpgradeToolConfig extends ItemConfig {

    public ItemUpgradeToolConfig(boolean upgrade) {
        super(
                ColossalChests._instance,
                "upgrade_tool" + (upgrade ? "" : "_reverse"),
                (eConfig) -> new ItemUpgradeTool(new Item.Properties()
                        .maxStackSize(1)
                        .group(ColossalChests._instance.getDefaultItemGroup()),
                        upgrade)
        );
    }

} public class CommonCapabilitiesModCompat implements IModCompat {

	@Override
	public String getId() {
		return Reference.MOD_COMMONCAPABILITIES;
	}

	@Override
	public boolean isEnabledDefault() {
		return true;
	}

	@Override
	public String getComment() {
		return "If slotless item handlers should be registered to colossal chests.";
	}

	@Override
	public ICompatInitializer createInitializer() {
		return () -> {
			CapabilityConstructorRegistry registry = ColossalChests._instance.getCapabilityConstructorRegistry();
			// Slotless item handler
			registry.registerTile(TileColossalChest.class,
					new SimpleCapabilityConstructor<ISlotlessItemHandler, TileColossalChest>() {
						@Override
						public Capability<ISlotlessItemHandler> getCapability() {
							return Capabilities.SLOTLESS_ITEMHANDLER;
						}

						@Nullable
						@Override
						public ICapabilityProvider createProvider(TileColossalChest host) {
							// Wrapper hack because Java otherwise complains that the optionalSlotlessItemHandler may not be initialized yet, and it won't compile.
							Wrapper<LazyOptional<ISlotlessItemHandler>> optionalSlotlessItemHandler = new Wrapper<>();
							optionalSlotlessItemHandler.set(LazyOptional.of(() -> {
								LazyOptional<IItemHandler> optionalItemHandler = host.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
								optionalItemHandler.addListener((o) -> optionalSlotlessItemHandler.get().invalidate());
								return new IndexedSlotlessItemHandlerWrapper(optionalItemHandler.orElse(null),
										(IndexedSlotlessItemHandlerWrapper.IInventoryIndexReference) host.getInventory());
							}));
							return new DefaultCapabilityProvider<>(this::getCapability, optionalSlotlessItemHandler.get());
						}
					});
			// Inventory state
			registry.registerTile(TileColossalChest.class,
					new SimpleCapabilityConstructor<IInventoryState, TileColossalChest>() {
						@Override
						public Capability<IInventoryState> getCapability() {
							return Capabilities.INVENTORY_STATE;
						}

						@Nullable
						@Override
						public ICapabilityProvider createProvider(TileColossalChest host) {
							return new DefaultCapabilityProvider<>(this::getCapability,
									LazyOptional.of(() -> () -> ((SimpleInventory) host.getInventory()).getState()));
						}
					});
		};
	}

} public class IronChestModCompat implements IModCompat {

	@OnlyIn(Dist.CLIENT)
	private void overrideTextures() {
		String prefix = "model/";
		RenderTileEntityColossalChest.TEXTURES_CHEST.put(ChestMaterial.COPPER,
				new ResourceLocation(Reference.MOD_IRONCHEST, prefix + "copper_chest"));
		RenderTileEntityColossalChest.TEXTURES_CHEST.put(ChestMaterial.IRON,
				new ResourceLocation(Reference.MOD_IRONCHEST, prefix + "iron_chest"));
		RenderTileEntityColossalChest.TEXTURES_CHEST.put(ChestMaterial.SILVER,
				new ResourceLocation(Reference.MOD_IRONCHEST, prefix + "silver_chest"));
		RenderTileEntityColossalChest.TEXTURES_CHEST.put(ChestMaterial.GOLD,
				new ResourceLocation(Reference.MOD_IRONCHEST, prefix + "gold_chest"));
		RenderTileEntityColossalChest.TEXTURES_CHEST.put(ChestMaterial.DIAMOND,
				new ResourceLocation(Reference.MOD_IRONCHEST, prefix + "diamond_chest"));
		RenderTileEntityColossalChest.TEXTURES_CHEST.put(ChestMaterial.OBSIDIAN,
				new ResourceLocation(Reference.MOD_IRONCHEST, prefix + "obsidian_chest"));
	}

	@Override
	public String getId() {
		return Reference.MOD_IRONCHEST;
	}

	@Override
	public boolean isEnabledDefault() {
		return true;
	}

	@Override
	public String getComment() {
		return "If the non-wood variants should use the textures of the Iron Chest mod.";
	}

	@Override
	public ICompatInitializer createInitializer() {
		return () -> {
			if(MinecraftHelpers.isClientSide()) {
				overrideTextures();
			}
		};
	}

} public class ClientProxy extends ClientProxyComponent {

	public ClientProxy() {
		super(new CommonProxy());
	}

	@Override
	public ModBase getMod() {
		return ColossalChests._instance;
	}
    
} public class CommonProxy extends CommonProxyComponent {

    @Override
    public ModBase getMod() {
        return ColossalChests._instance;
    }

    @Override
    public void registerPacketHandlers(PacketHandler packetHandler) {
        super.registerPacketHandlers(packetHandler);

        // Register packets.
        packetHandler.register(WindowItemsFragmentPacket.class);
        packetHandler.register(ClickWindowPacketOverride.class);
        packetHandler.register(SetSlotLarge.class);
    }

} public class RecipeConditionMetalVariantsSettingConfig extends RecipeConditionConfig<RecipeConditionMetalVariantsSetting> {

    public RecipeConditionMetalVariantsSettingConfig() {
        super(
                ColossalChests._instance,
                RecipeConditionMetalVariantsSetting.Serializer.INSTANCE
        );
    }

} public class TileColossalChestConfig extends TileEntityConfig<TileColossalChest> {

    public TileColossalChestConfig() {
        super(
                ColossalChests._instance,
                "colossal_chest",
                (eConfig) -> new TileEntityType<>(TileColossalChest::new,
                        ChestMaterial.VALUES.stream()
                                .map(ChestMaterial::getBlockCore)
                                .collect(Collectors.toSet()), null)
        );
    }

    @Override
    @OnlyIn(Dist.CLIENT)
    public void onRegistered() {
        super.onRegistered();
        ColossalChests._instance.getProxy().registerRenderer(getInstance(), RenderTileEntityColossalChest::new);
    }

} public class TileInterfaceConfig extends TileEntityConfig<TileInterface> {

    public TileInterfaceConfig() {
        super(
                ColossalChests._instance,
                "interface",
                (eConfig) -> new TileEntityType<>(TileInterface::new,
                        ChestMaterial.VALUES.stream()
                                .map(ChestMaterial::getBlockInterface)
                                .collect(Collectors.toSet()), null)
        );
    }

} public class TileUncolossalChestConfig extends TileEntityConfig<TileUncolossalChest> {

    public TileUncolossalChestConfig() {
        super(
                ColossalChests._instance,
                "uncolossal_chest",
                (eConfig) -> new TileEntityType<>(TileUncolossalChest::new,
                        Sets.newHashSet(RegistryEntries.BLOCK_UNCOLOSSAL_CHEST), null)
        );
    }

    @Override
    @OnlyIn(Dist.CLIENT)
    public void onRegistered() {
        super.onRegistered();
        ColossalChests._instance.getProxy().registerRenderer(getInstance(), RenderTileEntityUncolossalChest::new);
    }

}
import net.minecraft.item.ItemGroup;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import org.apache.logging.log4j.Level;
import org.cyclops.colossalchests.block.ChestMaterial;
import org.cyclops.colossalchests.block.ChestWallConfig;
import org.cyclops.colossalchests.block.ColossalChestConfig;
import org.cyclops.colossalchests.block.InterfaceConfig;
import org.cyclops.colossalchests.block.UncolossalChestConfig;
import org.cyclops.colossalchests.inventory.container.ContainerColossalChestConfig;
import org.cyclops.colossalchests.inventory.container.ContainerUncolossalChestConfig;
import org.cyclops.colossalchests.item.ItemUpgradeToolConfig;
import org.cyclops.colossalchests.modcompat.CommonCapabilitiesModCompat;
import org.cyclops.colossalchests.modcompat.IronChestModCompat;
import org.cyclops.colossalchests.proxy.ClientProxy;
import org.cyclops.colossalchests.proxy.CommonProxy;
import org.cyclops.colossalchests.recipe.condition.RecipeConditionMetalVariantsSettingConfig;
import org.cyclops.colossalchests.tileentity.TileColossalChestConfig;
import org.cyclops.colossalchests.tileentity.TileInterfaceConfig;
import org.cyclops.colossalchests.tileentity.TileUncolossalChestConfig;
import org.cyclops.cyclopscore.config.ConfigHandler;
import org.cyclops.cyclopscore.init.ItemGroupMod;
import org.cyclops.cyclopscore.init.ModBaseVersionable;
import org.cyclops.cyclopscore.modcompat.ModCompatLoader;
import org.cyclops.cyclopscore.proxy.IClientProxy;
import org.cyclops.cyclopscore.proxy.ICommonProxy;
package org.cyclops.colossalchests;


/**
 * The main mod class of this mod.
 * @author rubensworks
 *
 */
@Mod(Reference.MOD_ID)
public class ColossalChests extends ModBaseVersionable<ColossalChests> {
    
    /**
     * The unique instance of this mod.
     */
    public static ColossalChests _instance;

    public ColossalChests() {
        super(Reference.MOD_ID, (instance) -> _instance = instance);
    }

    @Override
    protected void loadModCompats(ModCompatLoader modCompatLoader) {
        modCompatLoader.addModCompat(new IronChestModCompat());
        modCompatLoader.addModCompat(new CommonCapabilitiesModCompat());