builder) {
132 | super.fillStateContainer(builder);
133 | builder.add(HORIZONTAL_FACING);
134 | builder.add(BURNING);
135 | }
136 |
137 | /**
138 | * Returns the blockstate with the given rotation from the passed blockstate.
139 | * If inapplicable, returns the passed blockstate.
140 | *
141 | * @deprecated call via {@link BlockState#rotate(Rotation)} whenever possible. Implementing/overriding is fine.
142 | */
143 | @Override
144 | public BlockState rotate(BlockState state, Rotation rot) {
145 | return state.with(HORIZONTAL_FACING, rot.rotate(state.get(HORIZONTAL_FACING)));
146 | }
147 |
148 | /**
149 | * Returns the blockstate with the given mirror of the passed blockstate.
150 | * If inapplicable, returns the passed blockstate.
151 | *
152 | * @deprecated call via {@link BlockState#mirror(Mirror)} whenever possible. Implementing/overriding is fine.
153 | */
154 | @Override
155 | public BlockState mirror(BlockState state, Mirror mirrorIn) {
156 | return state.rotate(mirrorIn.toRotation(state.get(HORIZONTAL_FACING)));
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/ClientForgeEventSubscriber.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import net.minecraftforge.api.distmarker.Dist;
5 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
6 |
7 | /**
8 | * Subscribe to events from the FORGE EventBus that should be handled on the PHYSICAL CLIENT side in this class
9 | *
10 | * @author Cadiboo
11 | */
12 | @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT)
13 | public final class ClientForgeEventSubscriber {
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/ClientModEventSubscriber.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.client.gui.ElectricFurnaceScreen;
5 | import io.github.cadiboo.examplemod.client.gui.HeatCollectorScreen;
6 | import io.github.cadiboo.examplemod.client.gui.ModFurnaceScreen;
7 | import io.github.cadiboo.examplemod.client.render.entity.WildBoarRenderer;
8 | import io.github.cadiboo.examplemod.client.render.tileentity.ElectricFurnaceTileEntityRenderer;
9 | import io.github.cadiboo.examplemod.client.render.tileentity.MiniModelTileEntityRenderer;
10 | import io.github.cadiboo.examplemod.init.ModContainerTypes;
11 | import io.github.cadiboo.examplemod.init.ModEntityTypes;
12 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes;
13 | import net.minecraft.client.gui.ScreenManager;
14 | import net.minecraftforge.api.distmarker.Dist;
15 | import net.minecraftforge.eventbus.api.SubscribeEvent;
16 | import net.minecraftforge.fml.DeferredWorkQueue;
17 | import net.minecraftforge.fml.client.registry.ClientRegistry;
18 | import net.minecraftforge.fml.client.registry.RenderingRegistry;
19 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
20 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
21 | import org.apache.logging.log4j.LogManager;
22 | import org.apache.logging.log4j.Logger;
23 |
24 | /**
25 | * Subscribe to events from the MOD EventBus that should be handled on the PHYSICAL CLIENT side in this class
26 | *
27 | * @author Cadiboo
28 | */
29 | @EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
30 | public final class ClientModEventSubscriber {
31 |
32 | private static final Logger LOGGER = LogManager.getLogger(ExampleMod.MODID + " Client Mod Event Subscriber");
33 |
34 | /**
35 | * We need to register our renderers on the client because rendering code does not exist on the server
36 | * and trying to use it on a dedicated server will crash the game.
37 | *
38 | * This method will be called by Forge when it is time for the mod to do its client-side setup
39 | * This method will always be called after the Registry events.
40 | * This means that all Blocks, Items, TileEntityTypes, etc. will all have been registered already
41 | */
42 | @SubscribeEvent
43 | public static void onFMLClientSetupEvent(final FMLClientSetupEvent event) {
44 |
45 | // Register TileEntity Renderers
46 | ClientRegistry.bindTileEntityRenderer(ModTileEntityTypes.MINI_MODEL.get(), MiniModelTileEntityRenderer::new);
47 | ClientRegistry.bindTileEntityRenderer(ModTileEntityTypes.ELECTRIC_FURNACE.get(), ElectricFurnaceTileEntityRenderer::new);
48 | LOGGER.debug("Registered TileEntity Renderers");
49 |
50 | // Register Entity Renderers
51 | RenderingRegistry.registerEntityRenderingHandler(ModEntityTypes.WILD_BOAR.get(), WildBoarRenderer::new);
52 | LOGGER.debug("Registered Entity Renderers");
53 |
54 | // Register ContainerType Screens
55 | // ScreenManager.registerFactory is not safe to call during parallel mod loading so we queue it to run later
56 | DeferredWorkQueue.runLater(() -> {
57 | ScreenManager.registerFactory(ModContainerTypes.HEAT_COLLECTOR.get(), HeatCollectorScreen::new);
58 | ScreenManager.registerFactory(ModContainerTypes.ELECTRIC_FURNACE.get(), ElectricFurnaceScreen::new);
59 | ScreenManager.registerFactory(ModContainerTypes.MOD_FURNACE.get(), ModFurnaceScreen::new);
60 | LOGGER.debug("Registered ContainerType Screens");
61 | });
62 |
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/gui/ElectricFurnaceScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.gui;
2 |
3 | import com.mojang.blaze3d.systems.RenderSystem;
4 | import io.github.cadiboo.examplemod.ExampleMod;
5 | import io.github.cadiboo.examplemod.container.ElectricFurnaceContainer;
6 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity;
7 | import net.minecraft.client.gui.screen.inventory.ContainerScreen;
8 | import net.minecraft.entity.player.PlayerInventory;
9 | import net.minecraft.util.ResourceLocation;
10 | import net.minecraft.util.text.ITextComponent;
11 | import net.minecraft.util.text.TranslationTextComponent;
12 | import net.minecraftforge.energy.EnergyStorage;
13 |
14 | /**
15 | * @author Cadiboo
16 | */
17 | public class ElectricFurnaceScreen extends ContainerScreen {
18 |
19 | private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/gui/container/electric_furnace.png");
20 |
21 | public ElectricFurnaceScreen(final ElectricFurnaceContainer container, final PlayerInventory inventory, final ITextComponent title) {
22 | super(container, inventory, title);
23 | }
24 |
25 | @Override
26 | public void render(final int mouseX, final int mouseY, final float partialTicks) {
27 | this.renderBackground();
28 | super.render(mouseX, mouseY, partialTicks);
29 | this.renderHoveredToolTip(mouseX, mouseY);
30 |
31 | int relMouseX = mouseX - this.guiLeft;
32 | int relMouseY = mouseY - this.guiTop;
33 | final ElectricFurnaceTileEntity tileEntity = this.container.tileEntity;
34 | boolean energyBarHovered = relMouseX > 151 && relMouseX < 166 && relMouseY > 10 && relMouseY < 76;
35 | if (energyBarHovered) {
36 | String tooltip = new TranslationTextComponent(
37 | "gui." + ExampleMod.MODID + ".energy",
38 | tileEntity.energy.getEnergyStored()
39 | ).getFormattedText();
40 | this.renderTooltip(tooltip, mouseX, mouseY);
41 | }
42 | boolean arrowHovered = relMouseX > 79 && relMouseX < 104 && relMouseY > 34 && relMouseY < 50;
43 | if (arrowHovered && tileEntity.maxSmeltTime > 0) {
44 | String tooltip = new TranslationTextComponent(
45 | "gui." + ExampleMod.MODID + ".smeltTimeProgress",
46 | tileEntity.smeltTimeLeft, tileEntity.maxSmeltTime
47 | ).getFormattedText();
48 | this.renderTooltip(tooltip, mouseX, mouseY);
49 | }
50 | }
51 |
52 | @Override
53 | protected void drawGuiContainerForegroundLayer(final int mouseX, final int mouseY) {
54 | super.drawGuiContainerForegroundLayer(mouseX, mouseY);
55 | // Copied from AbstractFurnaceScreen#drawGuiContainerForegroundLayer
56 | String s = this.title.getFormattedText();
57 | this.font.drawString(s, (float) (this.xSize / 2 - this.font.getStringWidth(s) / 2), 6.0F, 0x404040);
58 | this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float) (this.ySize - 96 + 2), 0x404040);
59 | }
60 |
61 | @Override
62 | protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) {
63 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
64 | getMinecraft().getTextureManager().bindTexture(BACKGROUND_TEXTURE);
65 | int startX = this.guiLeft;
66 | int startY = this.guiTop;
67 |
68 | // Screen#blit draws a part of the current texture (assumed to be 256x256) to the screen
69 | // The parameters are (x, y, u, v, width, height)
70 |
71 | this.blit(startX, startY, 0, 0, this.xSize, this.ySize);
72 |
73 | final ElectricFurnaceTileEntity tileEntity = container.tileEntity;
74 | if (tileEntity.energy.getEnergyStored() > 0) { // Draw energy bar
75 | int energyProgress = getEnergyProgressScaled();
76 | this.blit(
77 | startX + 152, startY + 10 + 65 - energyProgress,
78 | 176, 16,
79 | 14, energyProgress
80 | );
81 | }
82 | if (tileEntity.smeltTimeLeft > 0) {
83 | // Draw progress arrow
84 | int arrowWidth = getSmeltTimeScaled();
85 | this.blit(
86 | startX + 79, startY + 34,
87 | 176, 0,
88 | arrowWidth, 16
89 | );
90 | }
91 | }
92 |
93 | private int getEnergyProgressScaled() {
94 | final ElectricFurnaceTileEntity tileEntity = this.container.tileEntity;
95 | final EnergyStorage energy = tileEntity.energy;
96 | final int energyStored = energy.getEnergyStored();
97 | final int maxEnergyStored = energy.getMaxEnergyStored();
98 | return Math.round((float) energyStored / maxEnergyStored * 65); // 65 is the height of the arrow
99 | }
100 |
101 | private int getSmeltTimeScaled() {
102 | final ElectricFurnaceTileEntity tileEntity = this.container.tileEntity;
103 | final short smeltTimeLeft = tileEntity.smeltTimeLeft;
104 | final short maxSmeltTime = tileEntity.maxSmeltTime;
105 | if (smeltTimeLeft <= 0 || maxSmeltTime <= 0)
106 | return 0;
107 | return (maxSmeltTime - smeltTimeLeft) * 24 / maxSmeltTime; // 24 is the width of the arrow
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/gui/HeatCollectorScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.gui;
2 |
3 | import com.mojang.blaze3d.systems.RenderSystem;
4 | import io.github.cadiboo.examplemod.ExampleMod;
5 | import io.github.cadiboo.examplemod.container.HeatCollectorContainer;
6 | import io.github.cadiboo.examplemod.energy.SettableEnergyStorage;
7 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity;
8 | import net.minecraft.client.gui.screen.inventory.ContainerScreen;
9 | import net.minecraft.entity.player.PlayerInventory;
10 | import net.minecraft.util.ResourceLocation;
11 | import net.minecraft.util.text.ITextComponent;
12 | import net.minecraft.util.text.TranslationTextComponent;
13 |
14 | /**
15 | * @author Cadiboo
16 | */
17 | public class HeatCollectorScreen extends ContainerScreen {
18 |
19 | private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/gui/container/heat_collector.png");
20 |
21 | public HeatCollectorScreen(final HeatCollectorContainer container, final PlayerInventory inventory, final ITextComponent title) {
22 | super(container, inventory, title);
23 | }
24 |
25 | @Override
26 | public void render(final int mouseX, final int mouseY, final float partialTicks) {
27 | this.renderBackground();
28 | super.render(mouseX, mouseY, partialTicks);
29 | this.renderHoveredToolTip(mouseX, mouseY);
30 |
31 | int relMouseX = mouseX - this.guiLeft;
32 | int relMouseY = mouseY - this.guiTop;
33 | boolean energyBarHovered = relMouseX > 151 && relMouseX < 166 && relMouseY > 10 && relMouseY < 76;
34 | if (energyBarHovered) {
35 | String tooltip = new TranslationTextComponent(
36 | "gui." + ExampleMod.MODID + ".energy",
37 | this.container.tileEntity.energy.getEnergyStored()
38 | ).getFormattedText();
39 | this.renderTooltip(tooltip, mouseX, mouseY);
40 | }
41 | }
42 |
43 | @Override
44 | protected void drawGuiContainerForegroundLayer(final int mouseX, final int mouseY) {
45 | super.drawGuiContainerForegroundLayer(mouseX, mouseY);
46 | // Copied from AbstractFurnaceScreen#drawGuiContainerForegroundLayer
47 | String s = this.title.getFormattedText();
48 | this.font.drawString(s, (float) (this.xSize / 2 - this.font.getStringWidth(s) / 2), 6.0F, 0x404040);
49 | this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float) (this.ySize - 96 + 2), 0x404040);
50 | }
51 |
52 | @Override
53 | protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) {
54 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
55 | getMinecraft().getTextureManager().bindTexture(BACKGROUND_TEXTURE);
56 | int startX = this.guiLeft;
57 | int startY = this.guiTop;
58 |
59 | // Screen#blit draws a part of the current texture (assumed to be 256x256) to the screen
60 | // The parameters are (x, y, u, v, width, height)
61 |
62 | this.blit(startX, startY, 0, 0, this.xSize, this.ySize);
63 |
64 | final HeatCollectorTileEntity tileEntity = container.tileEntity;
65 |
66 | final SettableEnergyStorage energy = tileEntity.energy;
67 | final int energyStored = energy.getEnergyStored();
68 | if (energyStored > 0) { // Draw energy bar
69 | final int energyProgress = Math.round((float) energyStored / energy.getMaxEnergyStored() * 65);
70 | this.blit(
71 | startX + 152, startY + 10 + 65 - energyProgress,
72 | 176, 14,
73 | 14, energyProgress
74 | );
75 | }
76 |
77 | if (!tileEntity.inventory.getStackInSlot(HeatCollectorTileEntity.FUEL_SLOT).isEmpty()) // Draw flames
78 | this.blit(
79 | startX + 81, startY + 58,
80 | 176, 0,
81 | 14, 14
82 | );
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/gui/MiniModelScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.gui;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.client.render.MiniModel;
5 | import io.github.cadiboo.examplemod.init.ModBlocks;
6 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity;
7 | import net.minecraft.client.gui.screen.Screen;
8 | import net.minecraft.client.resources.I18n;
9 | import net.minecraftforge.fml.client.gui.widget.ExtendedButton;
10 |
11 | /**
12 | * A Screen for refreshing our MiniMode.
13 | * It contains two buttons "Refresh Mini Model" and "Done"
14 | *
15 | * @author Cadiboo
16 | */
17 | public class MiniModelScreen extends Screen {
18 |
19 | private final MiniModelTileEntity tileEntity;
20 |
21 | public MiniModelScreen(final MiniModelTileEntity tileEntity) {
22 | super(ModBlocks.MINI_MODEL.get().getNameTextComponent());
23 | this.tileEntity = tileEntity;
24 | }
25 |
26 | @Override
27 | public void render(final int mouseX, final int mouseY, final float partialTicks) {
28 | this.renderBackground();
29 | super.render(mouseX, mouseY, partialTicks);
30 | }
31 |
32 | @Override
33 | protected void init() {
34 | final int halfW = this.width / 2;
35 | final int halfH = this.height / 2;
36 | // "Refresh Mini Model" button rebuilds the tile's MiniModel
37 | this.addButton(new ExtendedButton(halfW - 150, halfH, 150, 20, I18n.format("gui." + ExampleMod.MODID + ".refresh_mini_model"),
38 | $ -> {
39 | final MiniModel miniModel = this.tileEntity.miniModel;
40 | if (miniModel != null)
41 | miniModel.compile();
42 | }
43 | ));
44 | // "Done" button exits the GUI
45 | this.addButton(new ExtendedButton(halfW, halfH, 150, 20, I18n.format("gui.done"),
46 | $ -> this.minecraft.displayGuiScreen(null)
47 | ));
48 | super.init();
49 | }
50 |
51 | @Override
52 | public boolean isPauseScreen() {
53 | return false; // Don't pause the game when this screen is open
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/gui/ModFurnaceScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.gui;
2 |
3 | import com.mojang.blaze3d.systems.RenderSystem;
4 | import io.github.cadiboo.examplemod.ExampleMod;
5 | import io.github.cadiboo.examplemod.container.ModFurnaceContainer;
6 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity;
7 | import net.minecraft.client.gui.screen.inventory.ContainerScreen;
8 | import net.minecraft.entity.player.PlayerInventory;
9 | import net.minecraft.util.ResourceLocation;
10 | import net.minecraft.util.text.ITextComponent;
11 | import net.minecraft.util.text.TranslationTextComponent;
12 |
13 | /**
14 | * @author Cadiboo
15 | */
16 | public class ModFurnaceScreen extends ContainerScreen {
17 |
18 | private static final ResourceLocation BACKGROUND_TEXTURE = new ResourceLocation("minecraft", "textures/gui/container/furnace.png");
19 |
20 | public ModFurnaceScreen(final ModFurnaceContainer container, final PlayerInventory inventory, final ITextComponent title) {
21 | super(container, inventory, title);
22 | }
23 |
24 | @Override
25 | public void render(final int mouseX, final int mouseY, final float partialTicks) {
26 | this.renderBackground();
27 | super.render(mouseX, mouseY, partialTicks);
28 | this.renderHoveredToolTip(mouseX, mouseY);
29 |
30 | int relMouseX = mouseX - this.guiLeft;
31 | int relMouseY = mouseY - this.guiTop;
32 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity;
33 | boolean arrowHovered = relMouseX > 79 && relMouseX < 104 && relMouseY > 34 && relMouseY < 50;
34 | if (arrowHovered && tileEntity.maxSmeltTime > 0) {
35 | String tooltip = new TranslationTextComponent(
36 | "gui." + ExampleMod.MODID + ".smeltTimeProgress",
37 | tileEntity.smeltTimeLeft, tileEntity.maxSmeltTime
38 | ).getFormattedText();
39 | this.renderTooltip(tooltip, mouseX, mouseY);
40 | }
41 | boolean fireHovered = relMouseX > 56 && relMouseX < 70 && relMouseY > 36 && relMouseY < 50;
42 | if (fireHovered && tileEntity.maxFuelBurnTime > 0) {
43 | String tooltip = new TranslationTextComponent(
44 | "gui." + ExampleMod.MODID + ".fuelBurnTimeProgress",
45 | tileEntity.fuelBurnTimeLeft, tileEntity.maxFuelBurnTime
46 | ).getFormattedText();
47 | this.renderTooltip(tooltip, mouseX, mouseY);
48 | }
49 | }
50 |
51 | @Override
52 | protected void drawGuiContainerForegroundLayer(final int mouseX, final int mouseY) {
53 | super.drawGuiContainerForegroundLayer(mouseX, mouseY);
54 | // Copied from AbstractFurnaceScreen#drawGuiContainerForegroundLayer
55 | String s = this.title.getFormattedText();
56 | this.font.drawString(s, (float) (this.xSize / 2 - this.font.getStringWidth(s) / 2), 6.0F, 0x404040);
57 | this.font.drawString(this.playerInventory.getDisplayName().getFormattedText(), 8.0F, (float) (this.ySize - 96 + 2), 0x404040);
58 |
59 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity;
60 | if (tileEntity.smeltTimeLeft > 0)
61 | this.font.drawString(tileEntity.smeltTimeLeft + " / " + tileEntity.maxSmeltTime, 8.0F, this.ySize, 0x404040);
62 | this.font.drawString(tileEntity.fuelBurnTimeLeft + " / " + tileEntity.maxFuelBurnTime, 8.0F, this.ySize + 14, 0x404040);
63 | }
64 |
65 | @Override
66 | protected void drawGuiContainerBackgroundLayer(final float partialTicks, final int mouseX, final int mouseY) {
67 | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
68 | getMinecraft().getTextureManager().bindTexture(BACKGROUND_TEXTURE);
69 | int startX = this.guiLeft;
70 | int startY = this.guiTop;
71 |
72 | // Screen#blit draws a part of the current texture (assumed to be 256x256) to the screen
73 | // The parameters are (x, y, u, v, width, height)
74 |
75 | this.blit(startX, startY, 0, 0, this.xSize, this.ySize);
76 |
77 | final ModFurnaceTileEntity tileEntity = container.tileEntity;
78 | if (tileEntity.smeltTimeLeft > 0) {
79 | // Draw progress arrow
80 | int arrowWidth = getSmeltTimeScaled();
81 | this.blit(
82 | startX + 79, startY + 34,
83 | 176, 14,
84 | arrowWidth, 14
85 | );
86 | }
87 | if (tileEntity.isBurning()) {
88 | // Draw flames
89 | int flameHeight = getFuelBurnTimeScaled();
90 | this.blit(
91 | startX + 56, startY + 50 - flameHeight,
92 | 176, 14 - flameHeight,
93 | 14, flameHeight
94 | );
95 | }
96 | }
97 |
98 | private int getSmeltTimeScaled() {
99 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity;
100 | final short smeltTimeLeft = tileEntity.smeltTimeLeft;
101 | final short maxSmeltTime = tileEntity.maxSmeltTime;
102 | if (smeltTimeLeft <= 0 || maxSmeltTime <= 0)
103 | return 0;
104 | return (maxSmeltTime - smeltTimeLeft) * 24 / maxSmeltTime; // 24 is the width of the arrow
105 | }
106 |
107 | private int getFuelBurnTimeScaled() {
108 | final ModFurnaceTileEntity tileEntity = this.container.tileEntity;
109 | if (tileEntity.maxFuelBurnTime <= 0)
110 | return 0;
111 | return tileEntity.fuelBurnTimeLeft * 16 / tileEntity.maxFuelBurnTime; // 14 is the height of the flames
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/render/MiniModel.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.render;
2 |
3 | import net.minecraft.tileentity.TileEntity;
4 |
5 | /**
6 | * A wrapper around ChunkRender.
7 | * Stores the a render of the chunk (16x16x16) surrounding a TileEntity
8 | * TODO: Make this work on 1.15
9 | *
10 | * @author Cadiboo
11 | */
12 | public class MiniModel {
13 |
14 | // // We only create one of these per cache, we reset it each time we rebuild
15 | // public final RegionRenderCacheBuilder regionRenderCacheBuilder;
16 | // private final ChunkRender chunkRender;
17 | // public ChunkRenderTask generator;
18 | private boolean isCompiled = false;
19 |
20 | // private MiniModel(final ChunkRender chunkRender, final RegionRenderCacheBuilder regionRenderCacheBuilder) {
21 | // this.chunkRender = chunkRender;
22 | // this.regionRenderCacheBuilder = regionRenderCacheBuilder;
23 | // }
24 |
25 | public static MiniModel forTileEntity(final TileEntity tileEntity) {
26 | // final ChunkRender chunkRender = new ChunkRender(tileEntity.getWorld(), Minecraft.getInstance().worldRenderer);
27 | // final BlockPos pos = tileEntity.getPos();
28 | //
29 | // // We want to render everything in a 16x16x16 radius, with the centre being the TileEntity
30 | // chunkRender.setPosition(pos.getX() - 8, pos.getY() - 8, pos.getZ() - 8);
31 | //
32 | // return new MiniModel(chunkRender, new RegionRenderCacheBuilder());
33 | return null;
34 | }
35 |
36 | /**
37 | * (re)build the render
38 | */
39 | public void compile() {
40 | // final ChunkRender chunkRender = this.chunkRender;
41 | // final RegionRenderCacheBuilder buffers = this.regionRenderCacheBuilder;
42 | //
43 | // final ChunkRenderTask generator = chunkRender.makeCompileTaskChunk();
44 | // this.generator = generator;
45 | //
46 | // // Setup generator
47 | // generator.setStatus(ChunkRenderTask.Status.COMPILING);
48 | // generator.setRegionRenderCacheBuilder(buffers);
49 | //
50 | // final Vec3d vec3d = Minecraft.getInstance().gameRenderer.getActiveRenderInfo().getProjectedView();
51 | //
52 | // // Rebuild the ChunkRender.
53 | // // This resets all the buffers it uses and renders every block in the chunk to the buffers
54 | // chunkRender.rebuildChunk((float) vec3d.x, (float) vec3d.y, (float) vec3d.z, generator);
55 | //
56 | // // ChunkRender#rebuildChunk increments this, we don't want it incremented so we decrement it.
57 | // --ChunkRender.renderChunksUpdated;
58 | //
59 | // // Set the translation of each buffer back to 0
60 | // final int length = BLOCK_RENDER_LAYERS.length;
61 | // for (int ordinal = 0; ordinal < length; ++ordinal) {
62 | // buffers.getBuilder(ordinal).setTranslation(0, 0, 0);
63 | // }
64 | // this.isBuilt = true;
65 | }
66 |
67 | public boolean isCompiled() {
68 | return isCompiled;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/render/entity/WildBoarRenderer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.render.entity;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.client.render.entity.layer.WildBoarSaddleLayer;
5 | import io.github.cadiboo.examplemod.entity.WildBoarEntity;
6 | import net.minecraft.client.renderer.entity.EntityRendererManager;
7 | import net.minecraft.client.renderer.entity.MobRenderer;
8 | import net.minecraft.client.renderer.entity.model.PigModel;
9 | import net.minecraft.util.ResourceLocation;
10 |
11 | /**
12 | * Handles rendering all WildBoar Entities.
13 | * The render method is called once each frame for every visible WildBoar.
14 | *
15 | * We use a PigModel in our renderer and simply change it's texture.
16 | *
17 | * @author Cadiboo
18 | */
19 | public class WildBoarRenderer extends MobRenderer> {
20 |
21 | private static final ResourceLocation WILD_BOAR_TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/entity/wild_boar/wild_boar.png");
22 |
23 | public WildBoarRenderer(final EntityRendererManager manager) {
24 | super(manager, new PigModel<>(), 0.7F);
25 | this.addLayer(new WildBoarSaddleLayer(this));
26 | }
27 |
28 | @Override
29 | public ResourceLocation getEntityTexture(final WildBoarEntity entity) {
30 | return WILD_BOAR_TEXTURE;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/render/entity/layer/WildBoarSaddleLayer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.render.entity.layer;
2 |
3 | import com.mojang.blaze3d.matrix.MatrixStack;
4 | import com.mojang.blaze3d.vertex.IVertexBuilder;
5 | import io.github.cadiboo.examplemod.ExampleMod;
6 | import io.github.cadiboo.examplemod.entity.WildBoarEntity;
7 | import net.minecraft.client.renderer.IRenderTypeBuffer;
8 | import net.minecraft.client.renderer.RenderType;
9 | import net.minecraft.client.renderer.entity.IEntityRenderer;
10 | import net.minecraft.client.renderer.entity.layers.LayerRenderer;
11 | import net.minecraft.client.renderer.entity.layers.SaddleLayer;
12 | import net.minecraft.client.renderer.entity.model.PigModel;
13 | import net.minecraft.client.renderer.texture.OverlayTexture;
14 | import net.minecraft.util.ResourceLocation;
15 |
16 | /**
17 | * Copy of {@link SaddleLayer} with tweaks to make it work for WildBoarEntity.
18 | *
19 | * @author Cadiboo
20 | */
21 | public class WildBoarSaddleLayer extends LayerRenderer> {
22 |
23 | private static final ResourceLocation TEXTURE = new ResourceLocation(ExampleMod.MODID, "textures/entity/wild_boar/wild_boar_saddle.png");
24 | private final PigModel pigModel = new PigModel<>(0.5F);
25 |
26 | public WildBoarSaddleLayer(IEntityRenderer> p_i50927_1_) {
27 | super(p_i50927_1_);
28 | }
29 |
30 | @Override
31 | public void render(MatrixStack matrixStack, IRenderTypeBuffer renderTypeBuffer, int light, WildBoarEntity entity, float p_225628_5_, float p_225628_6_, float p_225628_7_, float p_225628_8_, float p_225628_9_, float p_225628_10_) {
32 | if (entity.getSaddled()) {
33 | this.getEntityModel().setModelAttributes(this.pigModel);
34 | this.pigModel.setLivingAnimations(entity, p_225628_5_, p_225628_6_, p_225628_7_);
35 | this.pigModel.render(entity, p_225628_5_, p_225628_6_, p_225628_8_, p_225628_9_, p_225628_10_);
36 | IVertexBuilder buffer = renderTypeBuffer.getBuffer(RenderType.entityCutoutNoCull(TEXTURE));
37 | this.pigModel.render(matrixStack, buffer, light, OverlayTexture.DEFAULT_LIGHT, 1.0F, 1.0F, 1.0F, 1.0F);
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/render/tileentity/ElectricFurnaceTileEntityRenderer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.render.tileentity;
2 |
3 | import com.mojang.blaze3d.matrix.MatrixStack;
4 | import io.github.cadiboo.examplemod.config.ExampleModConfig;
5 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity;
6 | import net.minecraft.block.BlockState;
7 | import net.minecraft.block.Blocks;
8 | import net.minecraft.block.RedstoneTorchBlock;
9 | import net.minecraft.client.Minecraft;
10 | import net.minecraft.client.renderer.IRenderTypeBuffer;
11 | import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
12 | import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
13 |
14 | /**
15 | * Handles rendering all ElectricFurnace TileEntities.
16 | * The render method is called once each frame for every visible ElectricFurnace.
17 | *
18 | * @author Cadiboo
19 | */
20 | public class ElectricFurnaceTileEntityRenderer extends TileEntityRenderer {
21 |
22 | public ElectricFurnaceTileEntityRenderer(final TileEntityRendererDispatcher tileEntityRendererDispatcher) {
23 | super(tileEntityRendererDispatcher);
24 | }
25 |
26 | /**
27 | * Render our TileEntity
28 | */
29 | @Override
30 | public void render(final ElectricFurnaceTileEntity tileEntityIn, final float partialTicks, final MatrixStack matrixStack, final IRenderTypeBuffer renderTypeBuffer, final int packedLight, final int backupPackedLight) {
31 | // TODO: Fix this up to actually do the rendering I want
32 |
33 | final boolean hasEnergy = tileEntityIn.energy.getEnergyStored() >= ExampleModConfig.electricFurnaceEnergySmeltCostPerTick;
34 | final BlockState renderState = Blocks.REDSTONE_TORCH.getDefaultState()
35 | .with(RedstoneTorchBlock.LIT, hasEnergy);
36 | // Render the torch (We use the depreciated method because we don't have an IModelData instance and want to use the default one)
37 | Minecraft.getInstance().getBlockRendererDispatcher().renderBlock(renderState, matrixStack, renderTypeBuffer, packedLight, backupPackedLight);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/client/render/tileentity/MiniModelTileEntityRenderer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.client.render.tileentity;
2 |
3 | import com.mojang.blaze3d.matrix.MatrixStack;
4 | import com.mojang.blaze3d.systems.RenderSystem;
5 | import io.github.cadiboo.examplemod.client.render.MiniModel;
6 | import io.github.cadiboo.examplemod.config.ExampleModConfig;
7 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity;
8 | import net.minecraft.client.Minecraft;
9 | import net.minecraft.client.renderer.BufferBuilder;
10 | import net.minecraft.client.renderer.IRenderTypeBuffer;
11 | import net.minecraft.client.renderer.RenderHelper;
12 | import net.minecraft.client.renderer.WorldVertexBufferUploader;
13 | import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
14 | import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
15 | import org.lwjgl.opengl.GL11;
16 |
17 | import java.nio.ByteBuffer;
18 |
19 | /**
20 | * Handles rendering all MiniModel TileEntities.
21 | * The render method is called once each frame for every visible MiniModel.
22 | *
23 | * Renders a model of the surrounding blocks.
24 | * This should really probably not be in an examplemod for beginners,
25 | * but I added comments to it so its all good
26 | *
27 | * TODO: Update this to 1.15
28 | *
29 | * @author Cadiboo
30 | */
31 | public class MiniModelTileEntityRenderer extends TileEntityRenderer {
32 |
33 | public MiniModelTileEntityRenderer(final TileEntityRendererDispatcher tileEntityRendererDispatcher) {
34 | super(tileEntityRendererDispatcher);
35 | }
36 |
37 | /**
38 | * Render our TileEntity
39 | */
40 | @Override
41 | public void render(final MiniModelTileEntity tileEntityIn, final float partialTicks, final MatrixStack matrixStack, final IRenderTypeBuffer renderTypeBuffer, final int packedLight, final int backupPackedLight) {
42 |
43 | // final MiniModel miniModel = tileEntityIn.miniModel;
44 | //
45 | // if (miniModel == null)
46 | // return;
47 | //
48 | // if (!miniModel.isBuilt())
49 | // miniModel.rebuild();
50 | //
51 | // // Setup correct GL state
52 | //// this.field_228858_b_.textureManager.bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
53 | // RenderHelper.disableStandardItemLighting();
54 | // // Translucency
55 | // if (ExampleModConfig.modelTranslucency) {
56 | // RenderSystem.blendFunc(GL11.GL_ONE, GL11.GL_ONE);
57 | // } else {
58 | // RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
59 | // }
60 | // RenderSystem.enableBlend();
61 | //
62 | // if (Minecraft.isAmbientOcclusionEnabled()) {
63 | // RenderSystem.shadeModel(GL11.GL_SMOOTH);
64 | // } else {
65 | // RenderSystem.shadeModel(GL11.GL_FLAT);
66 | // }
67 | //
68 | // GlStateManager.pushMatrix();
69 | //
70 | // // Translate to render pos. The 0.5 is to translate into the centre of the block, rather than to the corner of it
71 | // GlStateManager.translated(x + 0.5, y + 0.5, z + 0.5);
72 | //
73 | // final double scale = ExampleModConfig.modelScale;
74 | // GlStateManager.scaled(scale, scale, scale);
75 | //
76 | // // Translate to start of render (our TileEntity is at its centre)
77 | // GlStateManager.translated(-8, -8, -8);
78 | //
79 | // // Render the buffers
80 | // renderChunkBuffers(miniModel.regionRenderCacheBuilder, miniModel.generator.getCompiledChunk());
81 | //
82 | // GlStateManager.popMatrix();
83 | //
84 | // // Clean up GL state
85 | // RenderHelper.enableStandardItemLighting();
86 |
87 | }
88 |
89 | /**
90 | * This renderer is a global renderer.
91 | * This means that it will always render, even if the player is not able to see it's block.
92 | * This is useful for rendering larger models or dynamically sized models.
93 | * The Beacon's beam is also a global renderer
94 | */
95 | @Override
96 | public boolean isGlobalRenderer(final MiniModelTileEntity te) {
97 | return true;
98 | }
99 |
100 | // /**
101 | // * Loops through every non-empty {@link BufferBuilder} in buffers and renders the buffer without resetting it
102 | // *
103 | // * @param buffers The {@link RegionRenderCacheBuilder} to get {@link BufferBuilder}s from
104 | // * @param compiledChunk The {@link CompiledChunk} to use to check if a layer has any rendered blocks
105 | // */
106 | // private void renderChunkBuffers(final RegionRenderCacheBuilder buffers, final CompiledChunk compiledChunk) {
107 | // final int length = BLOCK_RENDER_LAYERS.length;
108 | // // Render each buffer that has been used
109 | // for (int layerOrdinal = 0; layerOrdinal < length; ++layerOrdinal) {
110 | // if (!compiledChunk.isLayerEmpty(BLOCK_RENDER_LAYERS[layerOrdinal])) {
111 | // drawBufferWithoutResetting(buffers.getBuilder(layerOrdinal));
112 | // }
113 | // }
114 | // }
115 | //
116 | // /**
117 | // * This should work.
118 | // * Draws a BufferBuilder without resetting its internal data.
119 | // *
120 | // * @param bufferBuilder The BufferBuilder to draw (but not reset)
121 | // */
122 | // private void drawBufferWithoutResetting(final BufferBuilder bufferBuilder) {
123 | // // Get the internal data from the BufferBuilder (This resets the BufferBuilder's own copy of this data)
124 | // final ByteBuffer byteBuffer = bufferBuilder.getAndResetData().getSecond();
125 | // // Set the BufferBuilder's internal data to the original data
126 | // bufferBuilder.putBulkData(byteBuffer);
127 | // // Draw the BufferBuilder (This resets the BufferBuilder's data)
128 | // WorldVertexBufferUploader.draw(bufferBuilder);
129 | // // Set the BufferBuilder's internal data back to the original data
130 | // bufferBuilder.putBulkData(byteBuffer);
131 | // }
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/config/ClientConfig.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.config;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import net.minecraft.item.DyeColor;
5 | import net.minecraftforge.common.ForgeConfigSpec;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * For configuration settings that change the behaviour of code on the LOGICAL CLIENT.
12 | * This can be moved to an inner class of ExampleModConfig, but is separate because of personal preference and to keep the code organised
13 | *
14 | * @author Cadiboo
15 | */
16 | final class ClientConfig {
17 |
18 | final ForgeConfigSpec.BooleanValue clientBoolean;
19 | final ForgeConfigSpec.ConfigValue> clientStringList;
20 | final ForgeConfigSpec.EnumValue clientDyeColorEnum;
21 |
22 | final ForgeConfigSpec.BooleanValue modelTranslucency;
23 | final ForgeConfigSpec.DoubleValue modelScale;
24 |
25 | ClientConfig(final ForgeConfigSpec.Builder builder) {
26 | builder.push("general");
27 | clientBoolean = builder
28 | .comment("An example boolean in the client config")
29 | .translation(ExampleMod.MODID + ".config.clientBoolean")
30 | .define("clientBoolean", true);
31 | clientStringList = builder
32 | .comment("An example list of Strings in the client config")
33 | .translation(ExampleMod.MODID + ".config.clientStringList")
34 | .define("clientStringList", new ArrayList<>());
35 | clientDyeColorEnum = builder
36 | .comment("An example DyeColor enum in the client config")
37 | .translation(ExampleMod.MODID + ".config.clientDyeColorEnum")
38 | .defineEnum("clientDyeColorEnum", DyeColor.WHITE);
39 |
40 | modelTranslucency = builder
41 | .comment("If the model should be rendered translucent")
42 | .translation(ExampleMod.MODID + ".config.modelTranslucency")
43 | .define("modelTranslucency", true);
44 | modelScale = builder
45 | .comment("The scale to render the model at")
46 | .translation(ExampleMod.MODID + ".config.modelScale")
47 | .defineInRange("modelScale", 0.0625F, 0.0001F, 100F);
48 | builder.pop();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/config/ConfigHelper.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.config;
2 |
3 | import net.minecraftforge.fml.config.ModConfig;
4 |
5 | /**
6 | * This bakes the config values to normal fields
7 | *
8 | * @author Cadiboo
9 | * It can be merged into the main ExampleModConfig class, but is separate because of personal preference and to keep the code organised
10 | */
11 | public final class ConfigHelper {
12 |
13 | public static void bakeClient(final ModConfig config) {
14 | ExampleModConfig.clientBoolean = ConfigHolder.CLIENT.clientBoolean.get();
15 | ExampleModConfig.clientStringList = ConfigHolder.CLIENT.clientStringList.get();
16 | ExampleModConfig.clientDyeColorEnum = ConfigHolder.CLIENT.clientDyeColorEnum.get();
17 |
18 | ExampleModConfig.modelTranslucency = ConfigHolder.CLIENT.modelTranslucency.get();
19 | ExampleModConfig.modelScale = ConfigHolder.CLIENT.modelScale.get().floatValue();
20 | }
21 |
22 | public static void bakeServer(final ModConfig config) {
23 | ExampleModConfig.serverBoolean = ConfigHolder.SERVER.serverBoolean.get();
24 | ExampleModConfig.serverStringList = ConfigHolder.SERVER.serverStringList.get();
25 | ExampleModConfig.serverEnumDyeColor = ConfigHolder.SERVER.serverEnumDyeColor.get();
26 |
27 | ExampleModConfig.electricFurnaceEnergySmeltCostPerTick = ConfigHolder.SERVER.electricFurnaceEnergySmeltCostPerTick.get();
28 | ExampleModConfig.heatCollectorTransferAmountPerTick = ConfigHolder.SERVER.heatCollectorTransferAmountPerTick.get();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/config/ConfigHolder.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.config;
2 |
3 | import net.minecraftforge.common.ForgeConfigSpec;
4 | import org.apache.commons.lang3.tuple.Pair;
5 |
6 | /**
7 | * This holds the Client & Server Configs and the Client & Server ConfigSpecs.
8 | * It can be merged into the main ExampleModConfig class, but is separate because of personal preference and to keep the code organised
9 | *
10 | * @author Cadiboo
11 | */
12 | public final class ConfigHolder {
13 |
14 | public static final ForgeConfigSpec CLIENT_SPEC;
15 | public static final ForgeConfigSpec SERVER_SPEC;
16 | static final ClientConfig CLIENT;
17 | static final ServerConfig SERVER;
18 | static {
19 | {
20 | final Pair specPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new);
21 | CLIENT = specPair.getLeft();
22 | CLIENT_SPEC = specPair.getRight();
23 | }
24 | {
25 | final Pair specPair = new ForgeConfigSpec.Builder().configure(ServerConfig::new);
26 | SERVER = specPair.getLeft();
27 | SERVER_SPEC = specPair.getRight();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/config/ExampleModConfig.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.config;
2 |
3 | import net.minecraft.item.DyeColor;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * This holds the baked (runtime) values for our config.
9 | * These values should never be from changed outside this package.
10 | * This can be split into multiple classes (Server, Client, Player, Common)
11 | * but has been kept in one class for simplicity
12 | *
13 | * @author Cadiboo
14 | */
15 | public final class ExampleModConfig {
16 |
17 | // Client
18 | public static boolean clientBoolean;
19 | public static List clientStringList;
20 | public static DyeColor clientDyeColorEnum;
21 |
22 | public static boolean modelTranslucency;
23 | public static float modelScale;
24 |
25 | // Server
26 | public static boolean serverBoolean;
27 | public static List serverStringList;
28 | public static DyeColor serverEnumDyeColor;
29 |
30 | public static int electricFurnaceEnergySmeltCostPerTick = 100;
31 | public static int heatCollectorTransferAmountPerTick = 100;
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/config/ServerConfig.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.config;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import net.minecraft.item.DyeColor;
5 | import net.minecraftforge.common.ForgeConfigSpec;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | /**
11 | * For configuration settings that change the behaviour of code on the LOGICAL SERVER.
12 | * This can be moved to an inner class of ExampleModConfig, but is separate because of personal preference and to keep the code organised
13 | *
14 | * @author Cadiboo
15 | */
16 | final class ServerConfig {
17 |
18 | final ForgeConfigSpec.BooleanValue serverBoolean;
19 | final ForgeConfigSpec.ConfigValue> serverStringList;
20 | final ForgeConfigSpec.ConfigValue serverEnumDyeColor;
21 |
22 | final ForgeConfigSpec.IntValue electricFurnaceEnergySmeltCostPerTick;
23 | final ForgeConfigSpec.IntValue heatCollectorTransferAmountPerTick;
24 |
25 | ServerConfig(final ForgeConfigSpec.Builder builder) {
26 | builder.push("general");
27 | serverBoolean = builder
28 | .comment("An example boolean in the server config")
29 | .translation(ExampleMod.MODID + ".config.serverBoolean")
30 | .define("serverBoolean", true);
31 | serverStringList = builder
32 | .comment("An example list of Strings in the server config")
33 | .translation(ExampleMod.MODID + ".config.serverStringList")
34 | .define("serverStringList", new ArrayList<>());
35 | serverEnumDyeColor = builder
36 | .comment("An example enum DyeColor in the server config")
37 | .translation(ExampleMod.MODID + ".config.serverEnumDyeColor")
38 | .defineEnum("serverEnumDyeColor", DyeColor.WHITE);
39 |
40 | electricFurnaceEnergySmeltCostPerTick = builder
41 | .comment("How much energy for the Electric Furnace to consume to smelt an item per tick")
42 | .translation(ExampleMod.MODID + ".config.electricFurnaceEnergySmeltCostPerTick")
43 | .defineInRange("electricFurnaceEnergySmeltCostPerTick", 100, 0, Integer.MAX_VALUE);
44 | heatCollectorTransferAmountPerTick = builder
45 | .comment("How much energy for the Heat Collector to try and transfer in each direction per tick")
46 | .translation(ExampleMod.MODID + ".config.heatCollectorTransferAmountPerTick")
47 | .defineInRange("heatCollectorTransferAmountPerTick", 100, 0, Integer.MAX_VALUE);
48 | builder.pop();
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/container/ElectricFurnaceContainer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.container;
2 |
3 | import io.github.cadiboo.examplemod.init.ModBlocks;
4 | import io.github.cadiboo.examplemod.init.ModContainerTypes;
5 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity;
6 | import net.minecraft.client.network.play.ClientPlayNetHandler;
7 | import net.minecraft.entity.player.PlayerEntity;
8 | import net.minecraft.entity.player.PlayerInventory;
9 | import net.minecraft.entity.player.ServerPlayerEntity;
10 | import net.minecraft.inventory.container.Container;
11 | import net.minecraft.inventory.container.ContainerType;
12 | import net.minecraft.inventory.container.Slot;
13 | import net.minecraft.item.ItemStack;
14 | import net.minecraft.network.PacketBuffer;
15 | import net.minecraft.network.play.server.SWindowPropertyPacket;
16 | import net.minecraft.tileentity.TileEntity;
17 | import net.minecraft.util.IWorldPosCallable;
18 | import net.minecraft.util.IntReferenceHolder;
19 | import net.minecraftforge.fml.network.IContainerFactory;
20 | import net.minecraftforge.items.SlotItemHandler;
21 |
22 | import javax.annotation.Nonnull;
23 | import java.util.Objects;
24 |
25 | /**
26 | * Smelt time is synced with
27 | * Server: Each tick {@link #detectAndSendChanges()} is called ({@link ServerPlayerEntity#tick()})
28 | * Server: The (tracked) value of the tile's energy is updated ({@link #updateProgressBar(int, int)})
29 | * Server: If the value is different from the value last sent to the client ({@link IntReferenceHolder#isDirty()}),
30 | * it is synced to the client ({@link ServerPlayerEntity#sendWindowProperty(Container, int, int)})
31 | * Client: The sync packet is received ({@link ClientPlayNetHandler#handleWindowProperty(SWindowPropertyPacket)})
32 | * and the tracked value of is updated ({@link Container#updateProgressBar(int, int)})
33 | * Client: The tile's data is set to the new value
34 | *
35 | * @author Cadiboo
36 | */
37 | public class ElectricFurnaceContainer extends Container {
38 |
39 | public final ElectricFurnaceTileEntity tileEntity;
40 | private final IWorldPosCallable canInteractWithCallable;
41 |
42 | /**
43 | * Logical-client-side constructor, called from {@link ContainerType#create(IContainerFactory)}
44 | * Calls the logical-server-side constructor with the TileEntity at the pos in the PacketBuffer
45 | */
46 | public ElectricFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final PacketBuffer data) {
47 | this(windowId, playerInventory, getTileEntity(playerInventory, data));
48 | }
49 |
50 | /**
51 | * Constructor called logical-server-side from {@link ElectricFurnaceTileEntity#createMenu}
52 | * and logical-client-side from {@link #ElectricFurnaceContainer(int, PlayerInventory, PacketBuffer)}
53 | */
54 | public ElectricFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final ElectricFurnaceTileEntity tileEntity) {
55 | super(ModContainerTypes.ELECTRIC_FURNACE.get(), windowId);
56 | this.tileEntity = tileEntity;
57 | this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos());
58 |
59 | // Add tracking for data (Syncs to client/updates value when it changes)
60 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.smeltTimeLeft, v -> tileEntity.smeltTimeLeft = (short) v));
61 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.maxSmeltTime, v -> tileEntity.maxSmeltTime = (short) v));
62 |
63 | // Add all the slots for the tileEntity's inventory and the playerInventory to this container
64 |
65 | // Tile inventory slot(s)
66 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ElectricFurnaceTileEntity.INPUT_SLOT, 56, 35));
67 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ElectricFurnaceTileEntity.OUTPUT_SLOT, 116, 35));
68 |
69 | final int playerInventoryStartX = 8;
70 | final int playerInventoryStartY = 84;
71 | final int slotSizePlus2 = 18; // slots are 16x16, plus 2 (for spacing/borders) is 18x18
72 |
73 | // Player Top Inventory slots
74 | for (int row = 0; row < 3; ++row) {
75 | for (int column = 0; column < 9; ++column) {
76 | this.addSlot(new Slot(playerInventory, 9 + (row * 9) + column, playerInventoryStartX + (column * slotSizePlus2), playerInventoryStartY + (row * slotSizePlus2)));
77 | }
78 | }
79 |
80 | final int playerHotbarY = playerInventoryStartY + slotSizePlus2 * 3 + 4;
81 | // Player Hotbar slots
82 | for (int column = 0; column < 9; ++column) {
83 | this.addSlot(new Slot(playerInventory, column, playerInventoryStartX + (column * slotSizePlus2), playerHotbarY));
84 | }
85 | }
86 |
87 | private static ElectricFurnaceTileEntity getTileEntity(final PlayerInventory playerInventory, final PacketBuffer data) {
88 | Objects.requireNonNull(playerInventory, "playerInventory cannot be null!");
89 | Objects.requireNonNull(data, "data cannot be null!");
90 | final TileEntity tileAtPos = playerInventory.player.world.getTileEntity(data.readBlockPos());
91 | if (tileAtPos instanceof ElectricFurnaceTileEntity)
92 | return (ElectricFurnaceTileEntity) tileAtPos;
93 | throw new IllegalStateException("Tile entity is not correct! " + tileAtPos);
94 | }
95 |
96 | /**
97 | * Generic & dynamic version of {@link Container#transferStackInSlot(PlayerEntity, int)}.
98 | * Handle when the stack in slot {@code index} is shift-clicked.
99 | * Normally this moves the stack between the player inventory and the other inventory(s).
100 | *
101 | * @param player the player passed in
102 | * @param index the index passed in
103 | * @return the {@link ItemStack}
104 | */
105 | @Nonnull
106 | @Override
107 | public ItemStack transferStackInSlot(final PlayerEntity player, final int index) {
108 | ItemStack returnStack = ItemStack.EMPTY;
109 | final Slot slot = this.inventorySlots.get(index);
110 | if (slot != null && slot.getHasStack()) {
111 | final ItemStack slotStack = slot.getStack();
112 | returnStack = slotStack.copy();
113 |
114 | final int containerSlots = this.inventorySlots.size() - player.inventory.mainInventory.size();
115 | if (index < containerSlots) {
116 | if (!mergeItemStack(slotStack, containerSlots, this.inventorySlots.size(), true)) {
117 | return ItemStack.EMPTY;
118 | }
119 | } else if (!mergeItemStack(slotStack, 0, containerSlots, false)) {
120 | return ItemStack.EMPTY;
121 | }
122 | if (slotStack.getCount() == 0) {
123 | slot.putStack(ItemStack.EMPTY);
124 | } else {
125 | slot.onSlotChanged();
126 | }
127 | if (slotStack.getCount() == returnStack.getCount()) {
128 | return ItemStack.EMPTY;
129 | }
130 | slot.onTake(player, slotStack);
131 | }
132 | return returnStack;
133 | }
134 |
135 | @Override
136 | public boolean canInteractWith(@Nonnull final PlayerEntity player) {
137 | return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.ELECTRIC_FURNACE.get());
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/container/FunctionalIntReferenceHolder.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.container;
2 |
3 | import net.minecraft.util.IntReferenceHolder;
4 |
5 | import java.util.function.IntConsumer;
6 | import java.util.function.IntSupplier;
7 |
8 | /**
9 | * An {@link IntReferenceHolder} that uses {@link IntSupplier}s for its getter and setter
10 | *
11 | * @author Cadiboo
12 | */
13 | public class FunctionalIntReferenceHolder extends IntReferenceHolder {
14 |
15 | private final IntSupplier getter;
16 | private final IntConsumer setter;
17 |
18 | public FunctionalIntReferenceHolder(final IntSupplier getter, final IntConsumer setter) {
19 | this.getter = getter;
20 | this.setter = setter;
21 | }
22 |
23 | @Override
24 | public int get() {
25 | return this.getter.getAsInt();
26 | }
27 |
28 | @Override
29 | public void set(final int newValue) {
30 | this.setter.accept(newValue);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/container/HeatCollectorContainer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.container;
2 |
3 | import io.github.cadiboo.examplemod.init.ModBlocks;
4 | import io.github.cadiboo.examplemod.init.ModContainerTypes;
5 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity;
6 | import net.minecraft.entity.player.PlayerEntity;
7 | import net.minecraft.entity.player.PlayerInventory;
8 | import net.minecraft.inventory.container.Container;
9 | import net.minecraft.inventory.container.ContainerType;
10 | import net.minecraft.inventory.container.Slot;
11 | import net.minecraft.item.ItemStack;
12 | import net.minecraft.network.PacketBuffer;
13 | import net.minecraft.tileentity.TileEntity;
14 | import net.minecraft.util.IWorldPosCallable;
15 | import net.minecraftforge.fml.network.IContainerFactory;
16 | import net.minecraftforge.items.SlotItemHandler;
17 |
18 | import javax.annotation.Nonnull;
19 | import java.util.Objects;
20 |
21 | /**
22 | * @author Cadiboo
23 | */
24 | public class HeatCollectorContainer extends Container {
25 |
26 | public final HeatCollectorTileEntity tileEntity;
27 | private final IWorldPosCallable canInteractWithCallable;
28 |
29 | /**
30 | * Logical-client-side constructor, called from {@link ContainerType#create(IContainerFactory)}
31 | * Calls the logical-server-side constructor with the TileEntity at the pos in the PacketBuffer
32 | */
33 | public HeatCollectorContainer(final int windowId, final PlayerInventory playerInventory, final PacketBuffer data) {
34 | this(windowId, playerInventory, getTileEntity(playerInventory, data));
35 | }
36 |
37 | /**
38 | * Constructor called logical-server-side from {@link HeatCollectorTileEntity#createMenu}
39 | * and logical-client-side from {@link #HeatCollectorContainer(int, PlayerInventory, PacketBuffer)}
40 | */
41 | public HeatCollectorContainer(final int windowId, final PlayerInventory playerInventory, final HeatCollectorTileEntity tileEntity) {
42 | super(ModContainerTypes.HEAT_COLLECTOR.get(), windowId);
43 | this.tileEntity = tileEntity;
44 | this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos());
45 |
46 | // Add all the slots for the tileEntity's inventory and the playerInventory to this container
47 |
48 | // Tile inventory slot(s)
49 | this.addSlot(new SlotItemHandler(tileEntity.inventory, HeatCollectorTileEntity.FUEL_SLOT, 80, 35));
50 |
51 | final int playerInventoryStartX = 8;
52 | final int playerInventoryStartY = 84;
53 | final int slotSizePlus2 = 18; // slots are 16x16, plus 2 (for spacing/borders) is 18x18
54 |
55 | // Player Top Inventory slots
56 | for (int row = 0; row < 3; ++row) {
57 | for (int column = 0; column < 9; ++column) {
58 | this.addSlot(new Slot(playerInventory, 9 + (row * 9) + column, playerInventoryStartX + (column * slotSizePlus2), playerInventoryStartY + (row * slotSizePlus2)));
59 | }
60 | }
61 |
62 | final int playerHotbarY = playerInventoryStartY + slotSizePlus2 * 3 + 4;
63 | // Player Hotbar slots
64 | for (int column = 0; column < 9; ++column) {
65 | this.addSlot(new Slot(playerInventory, column, playerInventoryStartX + (column * slotSizePlus2), playerHotbarY));
66 | }
67 | }
68 |
69 | private static HeatCollectorTileEntity getTileEntity(final PlayerInventory playerInventory, final PacketBuffer data) {
70 | Objects.requireNonNull(playerInventory, "playerInventory cannot be null!");
71 | Objects.requireNonNull(data, "data cannot be null!");
72 | final TileEntity tileAtPos = playerInventory.player.world.getTileEntity(data.readBlockPos());
73 | if (tileAtPos instanceof HeatCollectorTileEntity)
74 | return (HeatCollectorTileEntity) tileAtPos;
75 | throw new IllegalStateException("Tile entity is not correct! " + tileAtPos);
76 | }
77 |
78 | /**
79 | * Generic & dynamic version of {@link Container#transferStackInSlot(PlayerEntity, int)}.
80 | * Handle when the stack in slot {@code index} is shift-clicked.
81 | * Normally this moves the stack between the player inventory and the other inventory(s).
82 | *
83 | * @param player the player passed in
84 | * @param index the index passed in
85 | * @return the {@link ItemStack}
86 | */
87 | @Nonnull
88 | @Override
89 | public ItemStack transferStackInSlot(final PlayerEntity player, final int index) {
90 | ItemStack returnStack = ItemStack.EMPTY;
91 | final Slot slot = this.inventorySlots.get(index);
92 | if (slot != null && slot.getHasStack()) {
93 | final ItemStack slotStack = slot.getStack();
94 | returnStack = slotStack.copy();
95 |
96 | final int containerSlots = this.inventorySlots.size() - player.inventory.mainInventory.size();
97 | if (index < containerSlots) {
98 | if (!mergeItemStack(slotStack, containerSlots, this.inventorySlots.size(), true)) {
99 | return ItemStack.EMPTY;
100 | }
101 | } else if (!mergeItemStack(slotStack, 0, containerSlots, false)) {
102 | return ItemStack.EMPTY;
103 | }
104 | if (slotStack.getCount() == 0) {
105 | slot.putStack(ItemStack.EMPTY);
106 | } else {
107 | slot.onSlotChanged();
108 | }
109 | if (slotStack.getCount() == returnStack.getCount()) {
110 | return ItemStack.EMPTY;
111 | }
112 | slot.onTake(player, slotStack);
113 | }
114 | return returnStack;
115 | }
116 |
117 | @Override
118 | public boolean canInteractWith(@Nonnull final PlayerEntity player) {
119 | return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.HEAT_COLLECTOR.get());
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/container/ModFurnaceContainer.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.container;
2 |
3 | import io.github.cadiboo.examplemod.init.ModBlocks;
4 | import io.github.cadiboo.examplemod.init.ModContainerTypes;
5 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity;
6 | import net.minecraft.client.network.play.ClientPlayNetHandler;
7 | import net.minecraft.entity.player.PlayerEntity;
8 | import net.minecraft.entity.player.PlayerInventory;
9 | import net.minecraft.entity.player.ServerPlayerEntity;
10 | import net.minecraft.inventory.container.Container;
11 | import net.minecraft.inventory.container.ContainerType;
12 | import net.minecraft.inventory.container.Slot;
13 | import net.minecraft.item.ItemStack;
14 | import net.minecraft.network.PacketBuffer;
15 | import net.minecraft.network.play.server.SWindowPropertyPacket;
16 | import net.minecraft.tileentity.TileEntity;
17 | import net.minecraft.util.IWorldPosCallable;
18 | import net.minecraft.util.IntReferenceHolder;
19 | import net.minecraftforge.fml.network.IContainerFactory;
20 | import net.minecraftforge.items.SlotItemHandler;
21 |
22 | import javax.annotation.Nonnull;
23 | import java.util.Objects;
24 |
25 | /**
26 | * Smelt time is synced with
27 | * Server: Each tick {@link #detectAndSendChanges()} is called ({@link ServerPlayerEntity#tick()})
28 | * Server: The (tracked) value of the tile's energy is updated ({@link #updateProgressBar(int, int)})
29 | * Server: If the value is different from the value last sent to the client ({@link IntReferenceHolder#isDirty()}),
30 | * it is synced to the client ({@link ServerPlayerEntity#sendWindowProperty(Container, int, int)})
31 | * Client: The sync packet is received ({@link ClientPlayNetHandler#handleWindowProperty(SWindowPropertyPacket)})
32 | * and the tracked value of is updated ({@link Container#updateProgressBar(int, int)})
33 | * Client: The tile's data is set to the new value
34 | *
35 | * @author Cadiboo
36 | */
37 | public class ModFurnaceContainer extends Container {
38 |
39 | public final ModFurnaceTileEntity tileEntity;
40 | private final IWorldPosCallable canInteractWithCallable;
41 |
42 | /**
43 | * Logical-client-side constructor, called from {@link ContainerType#create(IContainerFactory)}
44 | * Calls the logical-server-side constructor with the TileEntity at the pos in the PacketBuffer
45 | */
46 | public ModFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final PacketBuffer data) {
47 | this(windowId, playerInventory, getTileEntity(playerInventory, data));
48 | }
49 |
50 | /**
51 | * Constructor called logical-server-side from {@link ModFurnaceTileEntity#createMenu}
52 | * and logical-client-side from {@link #ModFurnaceContainer(int, PlayerInventory, PacketBuffer)}
53 | */
54 | public ModFurnaceContainer(final int windowId, final PlayerInventory playerInventory, final ModFurnaceTileEntity tileEntity) {
55 | super(ModContainerTypes.MOD_FURNACE.get(), windowId);
56 | this.tileEntity = tileEntity;
57 | this.canInteractWithCallable = IWorldPosCallable.of(tileEntity.getWorld(), tileEntity.getPos());
58 |
59 | // Add tracking for data (Syncs to client/updates value when it changes)
60 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.smeltTimeLeft, v -> tileEntity.smeltTimeLeft = (short) v));
61 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.maxSmeltTime, v -> tileEntity.maxSmeltTime = (short) v));
62 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.fuelBurnTimeLeft, v -> tileEntity.fuelBurnTimeLeft = (short) v));
63 | this.trackInt(new FunctionalIntReferenceHolder(() -> tileEntity.maxFuelBurnTime, v -> tileEntity.maxFuelBurnTime = (short) v));
64 |
65 | // Add all the slots for the tileEntity's inventory and the playerInventory to this container
66 |
67 | // Tile inventory slot(s)
68 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ModFurnaceTileEntity.FUEL_SLOT, 56, 53));
69 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ModFurnaceTileEntity.INPUT_SLOT, 56, 17));
70 | this.addSlot(new SlotItemHandler(tileEntity.inventory, ModFurnaceTileEntity.OUTPUT_SLOT, 116, 35));
71 |
72 | final int playerInventoryStartX = 8;
73 | final int playerInventoryStartY = 84;
74 | final int slotSizePlus2 = 18; // slots are 16x16, plus 2 (for spacing/borders) is 18x18
75 |
76 | // Player Top Inventory slots
77 | for (int row = 0; row < 3; ++row) {
78 | for (int column = 0; column < 9; ++column) {
79 | this.addSlot(new Slot(playerInventory, 9 + (row * 9) + column, playerInventoryStartX + (column * slotSizePlus2), playerInventoryStartY + (row * slotSizePlus2)));
80 | }
81 | }
82 |
83 | final int playerHotbarY = playerInventoryStartY + slotSizePlus2 * 3 + 4;
84 | // Player Hotbar slots
85 | for (int column = 0; column < 9; ++column) {
86 | this.addSlot(new Slot(playerInventory, column, playerInventoryStartX + (column * slotSizePlus2), playerHotbarY));
87 | }
88 | }
89 |
90 | private static ModFurnaceTileEntity getTileEntity(final PlayerInventory playerInventory, final PacketBuffer data) {
91 | Objects.requireNonNull(playerInventory, "playerInventory cannot be null!");
92 | Objects.requireNonNull(data, "data cannot be null!");
93 | final TileEntity tileAtPos = playerInventory.player.world.getTileEntity(data.readBlockPos());
94 | if (tileAtPos instanceof ModFurnaceTileEntity)
95 | return (ModFurnaceTileEntity) tileAtPos;
96 | throw new IllegalStateException("Tile entity is not correct! " + tileAtPos);
97 | }
98 |
99 | /**
100 | * Generic & dynamic version of {@link Container#transferStackInSlot(PlayerEntity, int)}.
101 | * Handle when the stack in slot {@code index} is shift-clicked.
102 | * Normally this moves the stack between the player inventory and the other inventory(s).
103 | *
104 | * @param player the player passed in
105 | * @param index the index passed in
106 | * @return the {@link ItemStack}
107 | */
108 | @Nonnull
109 | @Override
110 | public ItemStack transferStackInSlot(final PlayerEntity player, final int index) {
111 | ItemStack returnStack = ItemStack.EMPTY;
112 | final Slot slot = this.inventorySlots.get(index);
113 | if (slot != null && slot.getHasStack()) {
114 | final ItemStack slotStack = slot.getStack();
115 | returnStack = slotStack.copy();
116 |
117 | final int containerSlots = this.inventorySlots.size() - player.inventory.mainInventory.size();
118 | if (index < containerSlots) {
119 | if (!mergeItemStack(slotStack, containerSlots, this.inventorySlots.size(), true)) {
120 | return ItemStack.EMPTY;
121 | }
122 | } else if (!mergeItemStack(slotStack, 0, containerSlots, false)) {
123 | return ItemStack.EMPTY;
124 | }
125 | if (slotStack.getCount() == 0) {
126 | slot.putStack(ItemStack.EMPTY);
127 | } else {
128 | slot.onSlotChanged();
129 | }
130 | if (slotStack.getCount() == returnStack.getCount()) {
131 | return ItemStack.EMPTY;
132 | }
133 | slot.onTake(player, slotStack);
134 | }
135 | return returnStack;
136 | }
137 |
138 | @Override
139 | public boolean canInteractWith(@Nonnull final PlayerEntity player) {
140 | return isWithinUsableDistance(canInteractWithCallable, player, ModBlocks.MOD_FURNACE.get());
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/energy/SettableEnergyStorage.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.energy;
2 |
3 | import net.minecraftforge.energy.EnergyStorage;
4 |
5 | /**
6 | * @author Cadiboo
7 | */
8 | public class SettableEnergyStorage extends EnergyStorage {
9 |
10 | public SettableEnergyStorage(final int capacity) {
11 | super(capacity);
12 | }
13 |
14 | public SettableEnergyStorage(final int capacity, final int maxTransfer) {
15 | super(capacity, maxTransfer);
16 | }
17 |
18 | public SettableEnergyStorage(final int capacity, final int maxReceive, final int maxExtract) {
19 | super(capacity, maxReceive, maxExtract);
20 | }
21 |
22 | public SettableEnergyStorage(final int capacity, final int maxReceive, final int maxExtract, final int energy) {
23 | super(capacity, maxReceive, maxExtract, energy);
24 | }
25 |
26 | /**
27 | * @return The amount of energy that was put into the storage
28 | */
29 | public int setEnergy(final int maxSet) {
30 | return this.energy = Math.min(this.capacity, maxSet);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/entity/WildBoarEntity.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.entity;
2 |
3 | import net.minecraft.entity.AgeableEntity;
4 | import net.minecraft.entity.EntityType;
5 | import net.minecraft.entity.SharedMonsterAttributes;
6 | import net.minecraft.entity.passive.PigEntity;
7 | import net.minecraft.entity.passive.horse.AbstractHorseEntity;
8 | import net.minecraft.network.IPacket;
9 | import net.minecraft.world.World;
10 | import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
11 | import net.minecraftforge.fml.network.FMLPlayMessages;
12 | import net.minecraftforge.fml.network.NetworkHooks;
13 |
14 | /**
15 | * A Wild Boar entity.
16 | * Literally just a pig (with a different texture).
17 | * TODO: Will have more stuff added to it soon. (Will charge at player
18 | *
19 | * @author Cadiboo
20 | */
21 | public class WildBoarEntity extends PigEntity {
22 |
23 | public WildBoarEntity(final EntityType extends WildBoarEntity> entityType, final World world) {
24 | super(entityType, world);
25 | }
26 |
27 | @Override
28 | protected void registerAttributes() {
29 | super.registerAttributes();
30 |
31 | final double baseSpeed = this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getBaseValue();
32 | final double baseHealth = this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).getBaseValue();
33 | // Multiply base health and base speed by one and a half
34 | this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(baseSpeed * 1.5D);
35 | this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(baseHealth * 1.5D);
36 | }
37 |
38 | /**
39 | * Creates a child new entity from the parent entity.
40 | * Can be used to set additional on the child entity based on the parent.
41 | *
42 | * @param parent The entity that made this child
43 | * @return A new WildBoar
44 | * @see AbstractHorseEntity#setOffspringAttributes(AgeableEntity, AbstractHorseEntity)
45 | */
46 | @Override
47 | public WildBoarEntity createChild(final AgeableEntity parent) {
48 | // Use getType to support overrides in subclasses
49 | return (WildBoarEntity) getType().create(this.world);
50 | }
51 |
52 | /**
53 | * Called on the logical server to get a packet to send to the client containing data necessary to spawn your entity.
54 | * Using Forge's method instead of the default vanilla one allows extra stuff to work such as sending extra data,
55 | * using a non-default entity factory and having {@link IEntityAdditionalSpawnData} work.
56 | *
57 | * It is not actually necessary for our WildBoarEntity to use Forge's method as it doesn't need any of this extra
58 | * functionality, however, this is an example mod and many modders are unaware that Forge's method exists.
59 | *
60 | * @return The packet with data about your entity
61 | * @see FMLPlayMessages.SpawnEntity
62 | */
63 | @Override
64 | public IPacket> createSpawnPacket() {
65 | return NetworkHooks.getEntitySpawningPacket(this);
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/init/ModBlocks.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.init;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.block.ElectricFurnaceBlock;
5 | import io.github.cadiboo.examplemod.block.HeatCollectorBlock;
6 | import io.github.cadiboo.examplemod.block.MiniModelBlock;
7 | import io.github.cadiboo.examplemod.block.ModFurnaceBlock;
8 | import net.minecraft.block.Block;
9 | import net.minecraft.block.SoundType;
10 | import net.minecraft.block.material.Material;
11 | import net.minecraft.block.material.MaterialColor;
12 | import net.minecraftforge.fml.RegistryObject;
13 | import net.minecraftforge.registries.DeferredRegister;
14 | import net.minecraftforge.registries.ForgeRegistries;
15 |
16 | /**
17 | * Holds a list of all our {@link Block}s.
18 | * Suppliers that create Blocks are added to the DeferredRegister.
19 | * The DeferredRegister is then added to our mod event bus in our constructor.
20 | * When the Block Registry Event is fired by Forge and it is time for the mod to
21 | * register its Blocks, our Blocks are created and registered by the DeferredRegister.
22 | * The Block Registry Event will always be called before the Item registry is filled.
23 | * Note: This supports registry overrides.
24 | *
25 | * @author Cadiboo
26 | */
27 | public final class ModBlocks {
28 |
29 | public static final DeferredRegister BLOCKS = new DeferredRegister<>(ForgeRegistries.BLOCKS, ExampleMod.MODID);
30 |
31 | // This block has the ROCK material, meaning it needs at least a wooden pickaxe to break it. It is very similar to Iron Ore
32 | public static final RegistryObject EXAMPLE_ORE = BLOCKS.register("example_ore", () -> new Block(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.0F, 3.0F)));
33 | // This block has the IRON material, meaning it needs at least a stone pickaxe to break it. It is very similar to the Iron Block
34 | public static final RegistryObject EXAMPLE_BLOCK = BLOCKS.register("example_block", () -> new Block(Block.Properties.create(Material.IRON, MaterialColor.IRON).hardnessAndResistance(5.0F, 6.0F).sound(SoundType.METAL)));
35 | // This block has the MISCELLANEOUS material. It is very similar to the Redstone Torch
36 | public static final RegistryObject MINI_MODEL = BLOCKS.register("mini_model", () -> new MiniModelBlock(Block.Properties.create(Material.MISCELLANEOUS).hardnessAndResistance(1.5F).lightValue(13).doesNotBlockMovement()));
37 | // This block has the ROCK material, meaning it needs at least a wooden pickaxe to break it. It is very similar to the Furnace
38 | public static final RegistryObject HEAT_COLLECTOR = BLOCKS.register("heat_collector", () -> new HeatCollectorBlock(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.5F).lightValue(13)));
39 | public static final RegistryObject ELECTRIC_FURNACE = BLOCKS.register("electric_furnace", () -> new ElectricFurnaceBlock(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.5F)));
40 | public static final RegistryObject MOD_FURNACE = BLOCKS.register("mod_furnace", () -> new ModFurnaceBlock(Block.Properties.create(Material.ROCK).hardnessAndResistance(3.5F).lightValue(13)));
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/init/ModContainerTypes.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.init;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.container.ElectricFurnaceContainer;
5 | import io.github.cadiboo.examplemod.container.HeatCollectorContainer;
6 | import io.github.cadiboo.examplemod.container.ModFurnaceContainer;
7 | import net.minecraft.inventory.container.ContainerType;
8 | import net.minecraftforge.common.extensions.IForgeContainerType;
9 | import net.minecraftforge.fml.RegistryObject;
10 | import net.minecraftforge.registries.DeferredRegister;
11 | import net.minecraftforge.registries.ForgeRegistries;
12 |
13 | /**
14 | * Holds a list of all our {@link ContainerType}s.
15 | * Suppliers that create ContainerTypes are added to the DeferredRegister.
16 | * The DeferredRegister is then added to our mod event bus in our constructor.
17 | * When the ContainerType Registry Event is fired by Forge and it is time for the mod to
18 | * register its ContainerTypes, our ContainerTypes are created and registered by the DeferredRegister.
19 | * The ContainerType Registry Event will always be called after the Block and Item registries are filled.
20 | * Note: This supports registry overrides.
21 | *
22 | * @author Cadiboo
23 | */
24 | public final class ModContainerTypes {
25 |
26 | public static final DeferredRegister> CONTAINER_TYPES = new DeferredRegister<>(ForgeRegistries.CONTAINERS, ExampleMod.MODID);
27 |
28 | public static final RegistryObject> HEAT_COLLECTOR = CONTAINER_TYPES.register("heat_collector", () -> IForgeContainerType.create(HeatCollectorContainer::new));
29 | public static final RegistryObject> ELECTRIC_FURNACE = CONTAINER_TYPES.register("electric_furnace", () -> IForgeContainerType.create(ElectricFurnaceContainer::new));
30 | public static final RegistryObject> MOD_FURNACE = CONTAINER_TYPES.register("mod_furnace", () -> IForgeContainerType.create(ModFurnaceContainer::new));
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/init/ModEntityTypes.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.init;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.entity.WildBoarEntity;
5 | import net.minecraft.entity.EntityClassification;
6 | import net.minecraft.entity.EntityType;
7 | import net.minecraft.util.ResourceLocation;
8 | import net.minecraftforge.fml.RegistryObject;
9 | import net.minecraftforge.registries.DeferredRegister;
10 | import net.minecraftforge.registries.ForgeRegistries;
11 |
12 | /**
13 | * Holds a list of all our {@link EntityType}s.
14 | * Suppliers that create EntityTypes are added to the DeferredRegister.
15 | * The DeferredRegister is then added to our mod event bus in our constructor.
16 | * When the EntityType Registry Event is fired by Forge and it is time for the mod to
17 | * register its EntityTypes, our EntityTypes are created and registered by the DeferredRegister.
18 | * The EntityType Registry Event will always be called after the Block and Item registries are filled.
19 | * Note: This supports registry overrides.
20 | *
21 | * @author Cadiboo
22 | */
23 | public final class ModEntityTypes {
24 |
25 | public static final DeferredRegister> ENTITY_TYPES = new DeferredRegister<>(ForgeRegistries.ENTITIES, ExampleMod.MODID);
26 |
27 | public static final String WILD_BOAR_NAME = "wild_boar";
28 | public static final RegistryObject> WILD_BOAR = ENTITY_TYPES.register(WILD_BOAR_NAME, () ->
29 | EntityType.Builder.create(WildBoarEntity::new, EntityClassification.CREATURE)
30 | .size(EntityType.PIG.getWidth(), EntityType.PIG.getHeight())
31 | .build(new ResourceLocation(ExampleMod.MODID, WILD_BOAR_NAME).toString())
32 | );
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/init/ModItemGroups.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.init;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import net.minecraft.item.ItemGroup;
5 | import net.minecraft.item.ItemStack;
6 |
7 | import javax.annotation.Nonnull;
8 | import java.util.function.Supplier;
9 |
10 | /**
11 | * This class holds all our ItemGroups (Formerly called CreativeTabs).
12 | * Static initialisers are fine here.
13 | *
14 | * @author Cadiboo
15 | */
16 | public final class ModItemGroups {
17 |
18 | public static final ItemGroup MOD_ITEM_GROUP = new ModItemGroup(ExampleMod.MODID, () -> new ItemStack(ModItems.EXAMPLE_CRYSTAL.get()));
19 |
20 | public static final class ModItemGroup extends ItemGroup {
21 |
22 | @Nonnull
23 | private final Supplier iconSupplier;
24 |
25 | public ModItemGroup(@Nonnull final String name, @Nonnull final Supplier iconSupplier) {
26 | super(name);
27 | this.iconSupplier = iconSupplier;
28 | }
29 |
30 | @Override
31 | @Nonnull
32 | public ItemStack createIcon() {
33 | return iconSupplier.get();
34 | }
35 |
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/init/ModItems.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.init;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.item.ModdedSpawnEggItem;
5 | import net.minecraft.item.Item;
6 | import net.minecraftforge.fml.RegistryObject;
7 | import net.minecraftforge.registries.DeferredRegister;
8 | import net.minecraftforge.registries.ForgeRegistries;
9 |
10 | /**
11 | * Holds a list of all our {@link Item}s.
12 | * Suppliers that create Items are added to the DeferredRegister.
13 | * The DeferredRegister is then added to our mod event bus in our constructor.
14 | * When the Item Registry Event is fired by Forge and it is time for the mod to
15 | * register its Items, our Items are created and registered by the DeferredRegister.
16 | * The Item Registry Event will always be called after the Block registry is filled.
17 | * Note: This supports registry overrides.
18 | *
19 | * @author Cadiboo
20 | */
21 | public final class ModItems {
22 |
23 | public static final DeferredRegister- ITEMS = new DeferredRegister<>(ForgeRegistries.ITEMS, ExampleMod.MODID);
24 |
25 | // This is a very simple Item. It has no special properties except for being on our creative tab.
26 | public static final RegistryObject
- EXAMPLE_CRYSTAL = ITEMS.register("example_crystal", () -> new Item(new Item.Properties().group(ModItemGroups.MOD_ITEM_GROUP)));
27 | public static final RegistryObject WILD_BOAR_SPAWN_EGG = ITEMS.register("wild_boar_spawn_egg", () -> new ModdedSpawnEggItem(ModEntityTypes.WILD_BOAR, 0xF0A5A2, 0xA9672B, new Item.Properties().group(ModItemGroups.MOD_ITEM_GROUP)));
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/init/ModTileEntityTypes.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.init;
2 |
3 | import io.github.cadiboo.examplemod.ExampleMod;
4 | import io.github.cadiboo.examplemod.tileentity.ElectricFurnaceTileEntity;
5 | import io.github.cadiboo.examplemod.tileentity.HeatCollectorTileEntity;
6 | import io.github.cadiboo.examplemod.tileentity.MiniModelTileEntity;
7 | import io.github.cadiboo.examplemod.tileentity.ModFurnaceTileEntity;
8 | import net.minecraft.tileentity.TileEntityType;
9 | import net.minecraftforge.fml.RegistryObject;
10 | import net.minecraftforge.registries.DeferredRegister;
11 | import net.minecraftforge.registries.ForgeRegistries;
12 |
13 | /**
14 | * Holds a list of all our {@link TileEntityType}s.
15 | * Suppliers that create TileEntityTypes are added to the DeferredRegister.
16 | * The DeferredRegister is then added to our mod event bus in our constructor.
17 | * When the TileEntityType Registry Event is fired by Forge and it is time for the mod to
18 | * register its TileEntityTypes, our TileEntityTypes are created and registered by the DeferredRegister.
19 | * The TileEntityType Registry Event will always be called after the Block and Item registries are filled.
20 | * Note: This supports registry overrides.
21 | *
22 | * @author Cadiboo
23 | */
24 | public final class ModTileEntityTypes {
25 |
26 | public static final DeferredRegister> TILE_ENTITY_TYPES = new DeferredRegister<>(ForgeRegistries.TILE_ENTITIES, ExampleMod.MODID);
27 |
28 | // We don't have a datafixer for our TileEntities, so we pass null into build.
29 | public static final RegistryObject> MINI_MODEL = TILE_ENTITY_TYPES.register("mini_model", () ->
30 | TileEntityType.Builder.create(MiniModelTileEntity::new, ModBlocks.MINI_MODEL.get())
31 | .build(null)
32 | );
33 | public static final RegistryObject> HEAT_COLLECTOR = TILE_ENTITY_TYPES.register("heat_collector", () ->
34 | TileEntityType.Builder.create(HeatCollectorTileEntity::new, ModBlocks.HEAT_COLLECTOR.get())
35 | .build(null)
36 | );
37 | public static final RegistryObject> ELECTRIC_FURNACE = TILE_ENTITY_TYPES.register("electric_furnace", () ->
38 | TileEntityType.Builder.create(ElectricFurnaceTileEntity::new, ModBlocks.ELECTRIC_FURNACE.get())
39 | .build(null)
40 | );
41 | public static final RegistryObject> MOD_FURNACE = TILE_ENTITY_TYPES.register("mod_furnace", () ->
42 | TileEntityType.Builder.create(ModFurnaceTileEntity::new, ModBlocks.MOD_FURNACE.get())
43 | .build(null)
44 | );
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/item/ModdedSpawnEggItem.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.item;
2 |
3 | import net.minecraft.block.DispenserBlock;
4 | import net.minecraft.dispenser.DefaultDispenseItemBehavior;
5 | import net.minecraft.dispenser.IBlockSource;
6 | import net.minecraft.entity.EntityType;
7 | import net.minecraft.entity.SpawnReason;
8 | import net.minecraft.item.ItemStack;
9 | import net.minecraft.item.SpawnEggItem;
10 | import net.minecraft.nbt.CompoundNBT;
11 | import net.minecraft.util.Direction;
12 | import net.minecraftforge.common.util.Lazy;
13 | import net.minecraftforge.common.util.NonNullSupplier;
14 | import net.minecraftforge.fml.RegistryObject;
15 | import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
16 |
17 | import javax.annotation.Nullable;
18 | import java.util.ArrayList;
19 | import java.util.List;
20 | import java.util.Map;
21 |
22 | /**
23 | * Exists to work around a limitation with Spawn Eggs:
24 | * Spawn Eggs require an EntityType, but EntityTypes are created AFTER Items.
25 | * Therefore it is "impossible" for modded spawn eggs to exist.
26 | * This class gets around it by passing "null" to the SpawnEggItem constructor
27 | * and doing the initialisation after registry events have finished firing.
28 | *
29 | * TODO: Remove once Forge adds this stuff in itself.
30 | *
31 | * @author Cadiboo
32 | */
33 | public class ModdedSpawnEggItem extends SpawnEggItem {
34 |
35 | protected static final List UNADDED_EGGS = new ArrayList<>();
36 | private final Lazy extends EntityType>> entityTypeSupplier;
37 |
38 | public ModdedSpawnEggItem(final NonNullSupplier extends EntityType>> entityTypeSupplier, final int p_i48465_2_, final int p_i48465_3_, final Properties p_i48465_4_) {
39 | super(null, p_i48465_2_, p_i48465_3_, p_i48465_4_);
40 | this.entityTypeSupplier = Lazy.of(entityTypeSupplier::get);
41 | UNADDED_EGGS.add(this);
42 | }
43 |
44 | public ModdedSpawnEggItem(final RegistryObject extends EntityType>> entityTypeSupplier, final int p_i48465_2_, final int p_i48465_3_, final Properties p_i48465_4_) {
45 | super(null, p_i48465_2_, p_i48465_3_, p_i48465_4_);
46 | this.entityTypeSupplier = Lazy.of(entityTypeSupplier);
47 | UNADDED_EGGS.add(this);
48 | }
49 |
50 | /**
51 | * Adds all the supplier based spawn eggs to vanilla's map and registers an
52 | * IDispenseItemBehavior for each of them as normal spawn eggs have one
53 | * registered for each of them during {@link net.minecraft.dispenser.IDispenseItemBehavior#init()}
54 | * but supplier based ones won't have had their EntityTypes created yet.
55 | */
56 | public static void initUnaddedEggs() {
57 | final Map, SpawnEggItem> EGGS = ObfuscationReflectionHelper.getPrivateValue(SpawnEggItem.class, null, "field_195987_b");
58 | DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior() {
59 | public ItemStack dispenseStack(IBlockSource source, ItemStack stack) {
60 | Direction direction = source.getBlockState().get(DispenserBlock.FACING);
61 | EntityType> entitytype = ((SpawnEggItem) stack.getItem()).getType(stack.getTag());
62 | entitytype.spawn(source.getWorld(), stack, null, source.getBlockPos().offset(direction), SpawnReason.DISPENSER, direction != Direction.UP, false);
63 | stack.shrink(1);
64 | return stack;
65 | }
66 | };
67 | for (final SpawnEggItem egg : UNADDED_EGGS) {
68 | EGGS.put(egg.getType(null), egg);
69 | DispenserBlock.registerDispenseBehavior(egg, defaultDispenseItemBehavior);
70 | // ItemColors for each spawn egg don't need to be registered because this method is called before ItemColors is created
71 | }
72 | UNADDED_EGGS.clear();
73 | }
74 |
75 | @Override
76 | public EntityType> getType(@Nullable final CompoundNBT p_208076_1_) {
77 | return entityTypeSupplier.get();
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/tileentity/ElectricFurnaceTileEntity.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.tileentity;
2 |
3 | import io.github.cadiboo.examplemod.block.ElectricFurnaceBlock;
4 | import io.github.cadiboo.examplemod.config.ExampleModConfig;
5 | import io.github.cadiboo.examplemod.container.ElectricFurnaceContainer;
6 | import io.github.cadiboo.examplemod.energy.SettableEnergyStorage;
7 | import io.github.cadiboo.examplemod.init.ModBlocks;
8 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes;
9 | import net.minecraft.block.BlockState;
10 | import net.minecraft.entity.player.PlayerEntity;
11 | import net.minecraft.entity.player.PlayerInventory;
12 | import net.minecraft.inventory.IInventory;
13 | import net.minecraft.inventory.Inventory;
14 | import net.minecraft.inventory.container.Container;
15 | import net.minecraft.inventory.container.INamedContainerProvider;
16 | import net.minecraft.item.ItemStack;
17 | import net.minecraft.item.crafting.AbstractCookingRecipe;
18 | import net.minecraft.item.crafting.FurnaceRecipe;
19 | import net.minecraft.item.crafting.IRecipeType;
20 | import net.minecraft.nbt.CompoundNBT;
21 | import net.minecraft.network.NetworkManager;
22 | import net.minecraft.network.play.server.SUpdateTileEntityPacket;
23 | import net.minecraft.tileentity.AbstractFurnaceTileEntity;
24 | import net.minecraft.tileentity.ITickableTileEntity;
25 | import net.minecraft.tileentity.TileEntity;
26 | import net.minecraft.util.Direction;
27 | import net.minecraft.util.text.ITextComponent;
28 | import net.minecraft.util.text.TranslationTextComponent;
29 | import net.minecraftforge.common.capabilities.Capability;
30 | import net.minecraftforge.common.util.LazyOptional;
31 | import net.minecraftforge.energy.CapabilityEnergy;
32 | import net.minecraftforge.energy.EnergyStorage;
33 | import net.minecraftforge.fml.network.NetworkHooks;
34 | import net.minecraftforge.items.CapabilityItemHandler;
35 | import net.minecraftforge.items.IItemHandlerModifiable;
36 | import net.minecraftforge.items.ItemStackHandler;
37 | import net.minecraftforge.items.wrapper.RangedWrapper;
38 |
39 | import javax.annotation.Nonnull;
40 | import javax.annotation.Nullable;
41 | import java.util.Optional;
42 |
43 | /**
44 | * @author Cadiboo
45 | */
46 | public class ElectricFurnaceTileEntity extends TileEntity implements ITickableTileEntity, INamedContainerProvider {
47 |
48 | public static final int INPUT_SLOT = 0;
49 | public static final int OUTPUT_SLOT = 1;
50 |
51 | private static final String INVENTORY_TAG = "inventory";
52 | private static final String SMELT_TIME_LEFT_TAG = "smeltTimeLeft";
53 | private static final String MAX_SMELT_TIME_TAG = "maxSmeltTime";
54 | private static final String ENERGY_TAG = "energy";
55 | public final ItemStackHandler inventory = new ItemStackHandler(2) {
56 | @Override
57 | public boolean isItemValid(final int slot, @Nonnull final ItemStack stack) {
58 | switch (slot) {
59 | case INPUT_SLOT:
60 | return isInput(stack);
61 | case OUTPUT_SLOT:
62 | return isOutput(stack);
63 | default:
64 | return false;
65 | }
66 | }
67 |
68 | @Override
69 | protected void onContentsChanged(final int slot) {
70 | super.onContentsChanged(slot);
71 | // Mark the tile entity as having changed whenever its inventory changes.
72 | // "markDirty" tells vanilla that the chunk containing the tile entity has
73 | // changed and means the game will save the chunk to disk later.
74 | ElectricFurnaceTileEntity.this.markDirty();
75 | }
76 | };
77 | public final SettableEnergyStorage energy = new SettableEnergyStorage(100_000);
78 |
79 | // Store the capability lazy optionals as fields to keep the amount of objects we use to a minimum
80 | private final LazyOptional inventoryCapabilityExternal = LazyOptional.of(() -> this.inventory);
81 | // Machines (hoppers, pipes) connected to this furnace's top can only insert/extract items from the input slot
82 | private final LazyOptional inventoryCapabilityExternalUpAndSides = LazyOptional.of(() -> new RangedWrapper(this.inventory, INPUT_SLOT, INPUT_SLOT + 1));
83 | // Machines (hoppers, pipes) connected to this furnace's bottom can only insert/extract items from the output slot
84 | private final LazyOptional inventoryCapabilityExternalDown = LazyOptional.of(() -> new RangedWrapper(this.inventory, OUTPUT_SLOT, OUTPUT_SLOT + 1));
85 | private final LazyOptional energyCapabilityExternal = LazyOptional.of(() -> this.energy);
86 |
87 | public short smeltTimeLeft = -1;
88 | public short maxSmeltTime = -1;
89 | private int lastEnergy = -1;
90 |
91 | public ElectricFurnaceTileEntity() {
92 | super(ModTileEntityTypes.ELECTRIC_FURNACE.get());
93 | }
94 |
95 | /**
96 | * @return If the stack is not empty and has a smelting recipe associated with it
97 | */
98 | private boolean isInput(final ItemStack stack) {
99 | if (stack.isEmpty())
100 | return false;
101 | return getRecipe(stack).isPresent();
102 | }
103 |
104 | /**
105 | * @return If the stack's item is equal to the result of smelting our input
106 | */
107 | private boolean isOutput(final ItemStack stack) {
108 | final Optional result = getResult(inventory.getStackInSlot(INPUT_SLOT));
109 | return result.isPresent() && ItemStack.areItemsEqual(result.get(), stack);
110 | }
111 |
112 | /**
113 | * @return The smelting recipe for the input stack
114 | */
115 | private Optional getRecipe(final ItemStack input) {
116 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe so we make one here.
117 | return getRecipe(new Inventory(input));
118 | }
119 |
120 | /**
121 | * @return The smelting recipe for the inventory
122 | */
123 | private Optional getRecipe(final IInventory inventory) {
124 | return world.getRecipeManager().getRecipe(IRecipeType.SMELTING, inventory, world);
125 | }
126 |
127 | /**
128 | * @return The result of smelting the input stack
129 | */
130 | private Optional getResult(final ItemStack input) {
131 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe and
132 | // AbstractCookingRecipe#getCraftingResult() so we make one here.
133 | final Inventory dummyInventory = new Inventory(input);
134 | return getRecipe(dummyInventory).map(recipe -> recipe.getCraftingResult(dummyInventory));
135 | }
136 |
137 | /**
138 | * Called every tick to update our tile entity
139 | */
140 | @Override
141 | public void tick() {
142 | if (world == null || world.isRemote)
143 | return;
144 |
145 | // Energy will get pushed into our machine by generators/wires
146 |
147 | // Smelting code
148 |
149 | final ItemStack input = inventory.getStackInSlot(INPUT_SLOT).copy();
150 | final ItemStack result = getResult(input).orElse(ItemStack.EMPTY);
151 |
152 | if (!result.isEmpty() && isInput(input)) {
153 | final boolean canInsertResultIntoOutput = inventory.insertItem(OUTPUT_SLOT, result, true).isEmpty();
154 | if (canInsertResultIntoOutput) {
155 | // Energy consuming code
156 | final int energySmeltCostPerTick = ExampleModConfig.electricFurnaceEnergySmeltCostPerTick;
157 | boolean hasEnergy = false;
158 | if (energy.extractEnergy(energySmeltCostPerTick, true) == energySmeltCostPerTick) {
159 | hasEnergy = true;
160 | energy.extractEnergy(energySmeltCostPerTick, false);
161 | }
162 | if (hasEnergy) {
163 | if (smeltTimeLeft == -1) { // Item has not been smelted before
164 | smeltTimeLeft = maxSmeltTime = getSmeltTime(input);
165 | } else { // Item was already being smelted
166 | --smeltTimeLeft;
167 | if (smeltTimeLeft == 0) {
168 | inventory.insertItem(OUTPUT_SLOT, result, false);
169 | if (input.hasContainerItem())
170 | inventory.setStackInSlot(INPUT_SLOT, input.getContainerItem());
171 | else {
172 | input.shrink(1);
173 | inventory.setStackInSlot(INPUT_SLOT, input); // Update the data
174 | }
175 | smeltTimeLeft = -1; // Set to -1 so we smelt the next stack on the next tick
176 | }
177 | }
178 | } else // No energy -> add to smelt time left to simulate cooling
179 | if (smeltTimeLeft < maxSmeltTime)
180 | ++smeltTimeLeft;
181 | }
182 | } else // We have an invalid input stack (somehow)
183 | smeltTimeLeft = maxSmeltTime = -1;
184 |
185 | // If the energy has changed.
186 | if (lastEnergy != energy.getEnergyStored()) {
187 |
188 | // "markDirty" tells vanilla that the chunk containing the tile entity has
189 | // changed and means the game will save the chunk to disk later.
190 | this.markDirty();
191 |
192 | // Notify clients of a block update.
193 | // This will result in the packet from getUpdatePacket being sent to the client
194 | // and our energy being synced.
195 | final BlockState blockState = this.getBlockState();
196 | // Flag 2: Send the change to clients
197 | world.notifyBlockUpdate(pos, blockState, blockState, 2);
198 |
199 | // Update the last synced energy to the current energy
200 | lastEnergy = energy.getEnergyStored();
201 | }
202 |
203 | }
204 |
205 | /**
206 | * Mimics the code in {@link AbstractFurnaceTileEntity#func_214005_h()}
207 | *
208 | * @return The custom smelt time or 200 if there is no recipe for the input
209 | */
210 | private short getSmeltTime(final ItemStack input) {
211 | return getRecipe(input)
212 | .map(AbstractCookingRecipe::getCookTime)
213 | .orElse(200)
214 | .shortValue();
215 | }
216 |
217 | /**
218 | * Retrieves the Optional handler for the capability requested on the specific side.
219 | *
220 | * @param cap The capability to check
221 | * @param side The Direction to check from. CAN BE NULL! Null is defined to represent 'internal' or 'self'
222 | * @return The requested an optional holding the requested capability.
223 | */
224 | @Nonnull
225 | @Override
226 | public LazyOptional getCapability(@Nonnull final Capability cap, @Nullable final Direction side) {
227 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
228 | if (side == null)
229 | return inventoryCapabilityExternal.cast();
230 | switch (side) {
231 | case DOWN:
232 | return inventoryCapabilityExternalDown.cast();
233 | case UP:
234 | case NORTH:
235 | case SOUTH:
236 | case WEST:
237 | case EAST:
238 | return inventoryCapabilityExternalUpAndSides.cast();
239 | }
240 | }
241 | if (cap == CapabilityEnergy.ENERGY)
242 | return energyCapabilityExternal.cast();
243 | return super.getCapability(cap, side);
244 | }
245 |
246 | /**
247 | * Handle a packet created in {@link #getUpdatePacket()}
248 | */
249 | @Override
250 | public void onDataPacket(final NetworkManager net, final SUpdateTileEntityPacket pkt) {
251 | this.energy.setEnergy(pkt.getNbtCompound().getInt(ENERGY_TAG));
252 | }
253 |
254 | @Override
255 | public void onLoad() {
256 | super.onLoad();
257 | // We set this in onLoad instead of the constructor so that TileEntities
258 | // constructed from NBT (saved tile entities) have this set to the proper value
259 | if (world != null && !world.isRemote)
260 | lastEnergy = energy.getEnergyStored();
261 | }
262 |
263 | /**
264 | * Read saved data from disk into the tile.
265 | */
266 | @Override
267 | public void read(final CompoundNBT compound) {
268 | super.read(compound);
269 | this.inventory.deserializeNBT(compound.getCompound(INVENTORY_TAG));
270 | this.smeltTimeLeft = compound.getShort(SMELT_TIME_LEFT_TAG);
271 | this.maxSmeltTime = compound.getShort(MAX_SMELT_TIME_TAG);
272 | this.energy.setEnergy(compound.getInt(ENERGY_TAG));
273 | }
274 |
275 | /**
276 | * Write data from the tile into a compound tag for saving to disk.
277 | */
278 | @Nonnull
279 | @Override
280 | public CompoundNBT write(final CompoundNBT compound) {
281 | super.write(compound);
282 | compound.put(INVENTORY_TAG, this.inventory.serializeNBT());
283 | compound.putShort(SMELT_TIME_LEFT_TAG, this.smeltTimeLeft);
284 | compound.putShort(MAX_SMELT_TIME_TAG, this.maxSmeltTime);
285 | compound.putInt(ENERGY_TAG, this.energy.getEnergyStored());
286 | return compound;
287 | }
288 |
289 | /**
290 | * Retrieves packet to send to the client whenever this Tile Entity is re-synced via World#notifyBlockUpdate.
291 | * This packet comes back client-side in {@link #onDataPacket}
292 | */
293 | @Nullable
294 | public SUpdateTileEntityPacket getUpdatePacket() {
295 | final CompoundNBT tag = new CompoundNBT();
296 | tag.putInt(ENERGY_TAG, this.energy.getEnergyStored());
297 | // We pass 0 for tileEntityTypeIn because we have a modded TE. See ClientPlayNetHandler#handleUpdateTileEntity(SUpdateTileEntityPacket)
298 | return new SUpdateTileEntityPacket(this.pos, 0, tag);
299 | }
300 |
301 | /**
302 | * Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the
303 | * chunk or when many blocks change at once.
304 | * This compound comes back to you client-side in {@link #handleUpdateTag}
305 | * The default implementation ({@link TileEntity#handleUpdateTag}) calls {@link #writeInternal)}
306 | * which doesn't save any of our extra data so we override it to call {@link #write} instead
307 | */
308 | @Nonnull
309 | public CompoundNBT getUpdateTag() {
310 | return this.write(new CompoundNBT());
311 | }
312 |
313 | /**
314 | * Invalidates our tile entity
315 | */
316 | @Override
317 | public void remove() {
318 | super.remove();
319 | // We need to invalidate our capability references so that any cached references (by other mods) don't
320 | // continue to reference our capabilities and try to use them and/or prevent them from being garbage collected
321 | inventoryCapabilityExternal.invalidate();
322 | energyCapabilityExternal.invalidate();
323 | }
324 |
325 | @Nonnull
326 | @Override
327 | public ITextComponent getDisplayName() {
328 | return new TranslationTextComponent(ModBlocks.ELECTRIC_FURNACE.get().getTranslationKey());
329 | }
330 |
331 | /**
332 | * Called from {@link NetworkHooks#openGui}
333 | * (which is called from {@link ElectricFurnaceBlock#onBlockActivated} on the logical server)
334 | *
335 | * @return The logical-server-side Container for this TileEntity
336 | */
337 | @Nonnull
338 | @Override
339 | public Container createMenu(final int windowId, final PlayerInventory inventory, final PlayerEntity player) {
340 | return new ElectricFurnaceContainer(windowId, inventory, this);
341 | }
342 |
343 | }
344 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/tileentity/HeatCollectorTileEntity.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.tileentity;
2 |
3 | import io.github.cadiboo.examplemod.ModUtil;
4 | import io.github.cadiboo.examplemod.block.HeatCollectorBlock;
5 | import io.github.cadiboo.examplemod.config.ExampleModConfig;
6 | import io.github.cadiboo.examplemod.container.HeatCollectorContainer;
7 | import io.github.cadiboo.examplemod.energy.SettableEnergyStorage;
8 | import io.github.cadiboo.examplemod.init.ModBlocks;
9 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes;
10 | import net.minecraft.block.BlockState;
11 | import net.minecraft.block.Blocks;
12 | import net.minecraft.entity.player.PlayerEntity;
13 | import net.minecraft.entity.player.PlayerInventory;
14 | import net.minecraft.fluid.IFluidState;
15 | import net.minecraft.inventory.container.Container;
16 | import net.minecraft.inventory.container.INamedContainerProvider;
17 | import net.minecraft.item.ItemStack;
18 | import net.minecraft.nbt.CompoundNBT;
19 | import net.minecraft.network.NetworkManager;
20 | import net.minecraft.network.play.server.SUpdateTileEntityPacket;
21 | import net.minecraft.tags.FluidTags;
22 | import net.minecraft.tileentity.FurnaceTileEntity;
23 | import net.minecraft.tileentity.ITickableTileEntity;
24 | import net.minecraft.tileentity.TileEntity;
25 | import net.minecraft.util.Direction;
26 | import net.minecraft.util.math.BlockPos;
27 | import net.minecraft.util.text.ITextComponent;
28 | import net.minecraft.util.text.TranslationTextComponent;
29 | import net.minecraft.world.World;
30 | import net.minecraftforge.common.ForgeHooks;
31 | import net.minecraftforge.common.capabilities.Capability;
32 | import net.minecraftforge.common.util.LazyOptional;
33 | import net.minecraftforge.energy.CapabilityEnergy;
34 | import net.minecraftforge.energy.EnergyStorage;
35 | import net.minecraftforge.fml.network.NetworkHooks;
36 | import net.minecraftforge.items.CapabilityItemHandler;
37 | import net.minecraftforge.items.ItemStackHandler;
38 |
39 | import javax.annotation.Nonnull;
40 | import javax.annotation.Nullable;
41 |
42 | /**
43 | * @author Cadiboo
44 | */
45 | public class HeatCollectorTileEntity extends TileEntity implements ITickableTileEntity, INamedContainerProvider {
46 |
47 | public static final int FUEL_SLOT = 0;
48 |
49 | private static final String INVENTORY_TAG = "inventory";
50 | private static final String ENERGY_TAG = "energy";
51 |
52 | public final ItemStackHandler inventory = new ItemStackHandler(1) {
53 | @Override
54 | public boolean isItemValid(final int slot, @Nonnull final ItemStack stack) {
55 | return FurnaceTileEntity.isFuel(stack);
56 | }
57 |
58 | @Override
59 | protected void onContentsChanged(final int slot) {
60 | super.onContentsChanged(slot);
61 | // Mark the tile entity as having changed whenever its inventory changes.
62 | // "markDirty" tells vanilla that the chunk containing the tile entity has
63 | // changed and means the game will save the chunk to disk later.
64 | HeatCollectorTileEntity.this.markDirty();
65 | }
66 | };
67 | public final SettableEnergyStorage energy = new SettableEnergyStorage(100_000);
68 |
69 | // Store the capability lazy optionals as fields to keep the amount of objects we use to a minimum
70 | private final LazyOptional inventoryCapabilityExternal = LazyOptional.of(() -> this.inventory);
71 | private final LazyOptional energyCapabilityExternal = LazyOptional.of(() -> this.energy);
72 | private int lastEnergy = -1;
73 |
74 | public HeatCollectorTileEntity() {
75 | super(ModTileEntityTypes.HEAT_COLLECTOR.get());
76 | }
77 |
78 | @Override
79 | public void tick() {
80 |
81 | final World world = this.world;
82 | if (world == null || world.isRemote)
83 | return;
84 |
85 | final BlockPos pos = this.pos;
86 | final SettableEnergyStorage energy = this.energy;
87 |
88 | final ItemStack fuelStack = this.inventory.getStackInSlot(FUEL_SLOT).copy();
89 | if (!fuelStack.isEmpty()) {
90 | int energyToReceive = ForgeHooks.getBurnTime(fuelStack);
91 | // Only use the stack if we can receive 100% of the energy from it
92 | if (energy.receiveEnergy(energyToReceive, true) == energyToReceive) {
93 | energy.receiveEnergy(energyToReceive, false);
94 | if (fuelStack.hasContainerItem())
95 | inventory.setStackInSlot(FUEL_SLOT, fuelStack.getContainerItem());
96 | else {
97 | fuelStack.shrink(1);
98 | inventory.setStackInSlot(FUEL_SLOT, fuelStack); // Update the data
99 | }
100 | }
101 | }
102 |
103 | // Collect from all heat blocks in a 5 block radius
104 | for (final BlockPos blockPos : BlockPos.getAllInBoxMutable(pos.add(-5, -5, -5), pos.add(5, 5, 5))) {
105 | final BlockState blockState = world.getBlockState(blockPos);
106 | final IFluidState fluidState = world.getFluidState(blockPos);
107 |
108 | int fireEnergy = 0;
109 |
110 | if (blockState.getBlock() == Blocks.FIRE)
111 | fireEnergy += 20;
112 | else if (blockState.getBlock() == Blocks.CAMPFIRE)
113 | fireEnergy += 10;
114 |
115 | if (fluidState.isTagged(FluidTags.LAVA))
116 | fireEnergy += 50;
117 |
118 | if (fireEnergy > 0)
119 | energy.receiveEnergy(fireEnergy, false);
120 |
121 | }
122 |
123 | final int transferAmountPerTick = ExampleModConfig.heatCollectorTransferAmountPerTick;
124 | // Skip trying to transfer if there isn't enough energy to transfer
125 | if (energy.getEnergyStored() >= transferAmountPerTick) {
126 | for (Direction direction : ModUtil.DIRECTIONS) {
127 | final TileEntity te = world.getTileEntity(pos.offset(direction));
128 | if (te == null) {
129 | continue;
130 | }
131 | te.getCapability(CapabilityEnergy.ENERGY, direction).ifPresent(otherTileEnergy -> {
132 | if (!otherTileEnergy.canReceive()) {
133 | // Optimisation: Skip this tile if it can't receive
134 | return;
135 | }
136 | energy.extractEnergy(
137 | otherTileEnergy.receiveEnergy(
138 | energy.extractEnergy(100, true),
139 | false
140 | ),
141 | false
142 | );
143 | });
144 | }
145 | }
146 |
147 | // If the energy has changed.
148 | if (lastEnergy != energy.getEnergyStored()) {
149 |
150 | // "markDirty" tells vanilla that the chunk containing the tile entity has
151 | // changed and means the game will save the chunk to disk later.
152 | this.markDirty();
153 |
154 | // Notify clients of a block update.
155 | // This will result in the packet from getUpdatePacket being sent to the client
156 | // and our energy being synced.
157 | final BlockState blockState = this.getBlockState();
158 | // Flag 2: Send the change to clients
159 | world.notifyBlockUpdate(pos, blockState, blockState, 2);
160 |
161 | // Update the last synced energy to the current energy
162 | lastEnergy = energy.getEnergyStored();
163 | }
164 |
165 | }
166 |
167 | @Nonnull
168 | @Override
169 | public LazyOptional getCapability(@Nonnull final Capability cap, @Nullable final Direction side) {
170 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
171 | return inventoryCapabilityExternal.cast();
172 | if (cap == CapabilityEnergy.ENERGY)
173 | return energyCapabilityExternal.cast();
174 | return super.getCapability(cap, side);
175 | }
176 |
177 | /**
178 | * Handle a packet created in {@link #getUpdatePacket()}
179 | */
180 | @Override
181 | public void onDataPacket(final NetworkManager net, final SUpdateTileEntityPacket pkt) {
182 | this.energy.setEnergy(pkt.getNbtCompound().getInt(ENERGY_TAG));
183 | }
184 |
185 | @Override
186 | public void onLoad() {
187 | super.onLoad();
188 | // We set this in onLoad instead of the constructor so that TileEntities
189 | // constructed from NBT (saved tile entities) have this set to the proper value
190 | if (world != null && !world.isRemote)
191 | lastEnergy = energy.getEnergyStored();
192 | }
193 |
194 | /**
195 | * Read saved data from disk into the tile.
196 | */
197 | @Override
198 | public void read(final CompoundNBT compound) {
199 | super.read(compound);
200 | this.inventory.deserializeNBT(compound.getCompound(INVENTORY_TAG));
201 | this.energy.setEnergy(compound.getInt(ENERGY_TAG));
202 | }
203 |
204 | /**
205 | * Write data from the tile into a compound tag for saving to disk.
206 | */
207 | @Nonnull
208 | @Override
209 | public CompoundNBT write(final CompoundNBT compound) {
210 | super.write(compound);
211 | compound.put(INVENTORY_TAG, this.inventory.serializeNBT());
212 | compound.putInt(ENERGY_TAG, this.energy.getEnergyStored());
213 | return compound;
214 | }
215 |
216 | /**
217 | * Retrieves packet to send to the client whenever this Tile Entity is re-synced via World#notifyBlockUpdate.
218 | * This packet comes back client-side in {@link #onDataPacket}
219 | */
220 | @Nullable
221 | public SUpdateTileEntityPacket getUpdatePacket() {
222 | final CompoundNBT tag = new CompoundNBT();
223 | tag.putInt(ENERGY_TAG, this.energy.getEnergyStored());
224 | return new SUpdateTileEntityPacket(this.pos, 0, tag);
225 | }
226 |
227 | /**
228 | * Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the
229 | * chunk or when many blocks change at once.
230 | * This compound comes back to you client-side in {@link #handleUpdateTag}
231 | * The default implementation ({@link TileEntity#handleUpdateTag}) calls {@link #writeInternal)}
232 | * which doesn't save any of our extra data so we override it to call {@link #write} instead
233 | */
234 | @Nonnull
235 | public CompoundNBT getUpdateTag() {
236 | return this.write(new CompoundNBT());
237 | }
238 |
239 | /**
240 | * Invalidates our tile entity
241 | */
242 | @Override
243 | public void remove() {
244 | super.remove();
245 | // We need to invalidate our capability references so that any cached references (by other mods) don't
246 | // continue to reference our capabilities and try to use them and/or prevent them from being garbage collected
247 | inventoryCapabilityExternal.invalidate();
248 | energyCapabilityExternal.invalidate();
249 | }
250 |
251 | @Nonnull
252 | @Override
253 | public ITextComponent getDisplayName() {
254 | return new TranslationTextComponent(ModBlocks.HEAT_COLLECTOR.get().getTranslationKey());
255 | }
256 |
257 | /**
258 | * Called from {@link NetworkHooks#openGui}
259 | * (which is called from {@link HeatCollectorBlock#onBlockActivated} on the logical server)
260 | *
261 | * @return The logical-server-side Container for this TileEntity
262 | */
263 | @Nonnull
264 | @Override
265 | public Container createMenu(final int windowId, final PlayerInventory inventory, final PlayerEntity player) {
266 | return new HeatCollectorContainer(windowId, inventory, this);
267 | }
268 |
269 | }
270 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/tileentity/MiniModelTileEntity.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.tileentity;
2 |
3 | import io.github.cadiboo.examplemod.client.render.MiniModel;
4 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes;
5 | import net.minecraft.tileentity.TileEntity;
6 | import net.minecraft.tileentity.TileEntityType;
7 | import net.minecraft.util.math.AxisAlignedBB;
8 | import net.minecraft.world.World;
9 | import net.minecraftforge.api.distmarker.Dist;
10 | import net.minecraftforge.api.distmarker.OnlyIn;
11 |
12 | import javax.annotation.Nullable;
13 |
14 | /**
15 | * This is the TileEntity for our MiniModel.
16 | * It stores the render data for its surroundings.
17 | *
18 | * Rendering code only exists on the client distribution so @OnlyIn is used a lot.
19 | * The @OnlyIn annotation makes it so that a class/field/method annotated with it does not
20 | * exist on the other distribution. This is important as TileEntities exist on both distributions
21 | * but rendering only happens on the client.
22 | * As a general rule you should avoid using @OnlyIn unless it is absolutely necessary.
23 | *
24 | * @author Cadiboo
25 | */
26 | public class MiniModelTileEntity extends TileEntity {
27 |
28 | @Nullable // May be accessed before onLoad
29 | // @OnlyIn(Dist.CLIENT) Makes it so this field will be removed from the class on the PHYSICAL SERVER
30 | // This is because we only want the MiniModel on the physical client - its rendering only.
31 | @OnlyIn(Dist.CLIENT)
32 | public MiniModel miniModel;
33 |
34 | // Constructor without hardcoded TileEntityType so that subclasses can use their own.
35 | // Alternatively, subclasses can also override getType if a hardcoded type is used in a superclass' constructor
36 | public MiniModelTileEntity(final TileEntityType> type) {
37 | super(type);
38 | }
39 |
40 | public MiniModelTileEntity() {
41 | this(ModTileEntityTypes.MINI_MODEL.get());
42 | }
43 |
44 | // @OnlyIn(Dist.CLIENT) Makes it so this method will be removed from the class on the PHYSICAL SERVER
45 | // This is because we only want the MiniModel on the physical client - its rendering only.
46 | @OnlyIn(Dist.CLIENT)
47 | @Override
48 | public void onLoad() {
49 | super.onLoad();
50 | World world = getWorld();
51 | if (world == null || !world.isRemote)
52 | return; // Return if the world is null or if we are on the logical server
53 | miniModel = MiniModel.forTileEntity(this);
54 | }
55 |
56 | @Override
57 | public AxisAlignedBB getRenderBoundingBox() {
58 | // This, combined with isGlobalRenderer in the TileEntityRenderer makes it so that the
59 | // render does not disappear if the player can't see the block
60 | // This is useful for rendering larger models or dynamically sized models
61 | return INFINITE_EXTENT_AABB;
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/io/github/cadiboo/examplemod/tileentity/ModFurnaceTileEntity.java:
--------------------------------------------------------------------------------
1 | package io.github.cadiboo.examplemod.tileentity;
2 |
3 | import io.github.cadiboo.examplemod.block.ElectricFurnaceBlock;
4 | import io.github.cadiboo.examplemod.block.ModFurnaceBlock;
5 | import io.github.cadiboo.examplemod.container.ModFurnaceContainer;
6 | import io.github.cadiboo.examplemod.init.ModBlocks;
7 | import io.github.cadiboo.examplemod.init.ModTileEntityTypes;
8 | import net.minecraft.block.BlockState;
9 | import net.minecraft.entity.player.PlayerEntity;
10 | import net.minecraft.entity.player.PlayerInventory;
11 | import net.minecraft.inventory.IInventory;
12 | import net.minecraft.inventory.Inventory;
13 | import net.minecraft.inventory.container.Container;
14 | import net.minecraft.inventory.container.INamedContainerProvider;
15 | import net.minecraft.item.ItemStack;
16 | import net.minecraft.item.crafting.AbstractCookingRecipe;
17 | import net.minecraft.item.crafting.FurnaceRecipe;
18 | import net.minecraft.item.crafting.IRecipeType;
19 | import net.minecraft.nbt.CompoundNBT;
20 | import net.minecraft.tileentity.AbstractFurnaceTileEntity;
21 | import net.minecraft.tileentity.FurnaceTileEntity;
22 | import net.minecraft.tileentity.ITickableTileEntity;
23 | import net.minecraft.tileentity.TileEntity;
24 | import net.minecraft.util.Direction;
25 | import net.minecraft.util.text.ITextComponent;
26 | import net.minecraft.util.text.TranslationTextComponent;
27 | import net.minecraftforge.common.ForgeHooks;
28 | import net.minecraftforge.common.capabilities.Capability;
29 | import net.minecraftforge.common.util.LazyOptional;
30 | import net.minecraftforge.fml.network.NetworkHooks;
31 | import net.minecraftforge.items.CapabilityItemHandler;
32 | import net.minecraftforge.items.IItemHandlerModifiable;
33 | import net.minecraftforge.items.ItemStackHandler;
34 | import net.minecraftforge.items.wrapper.RangedWrapper;
35 |
36 | import javax.annotation.Nonnull;
37 | import javax.annotation.Nullable;
38 | import java.util.Optional;
39 |
40 | /**
41 | * @author Cadiboo
42 | */
43 | public class ModFurnaceTileEntity extends TileEntity implements ITickableTileEntity, INamedContainerProvider {
44 |
45 | public static final int FUEL_SLOT = 0;
46 | public static final int INPUT_SLOT = 1;
47 | public static final int OUTPUT_SLOT = 2;
48 |
49 | private static final String INVENTORY_TAG = "inventory";
50 | private static final String SMELT_TIME_LEFT_TAG = "smeltTimeLeft";
51 | private static final String MAX_SMELT_TIME_TAG = "maxSmeltTime";
52 | private static final String FUEL_BURN_TIME_LEFT_TAG = "fuelBurnTimeLeft";
53 | private static final String MAX_FUEL_BURN_TIME_TAG = "maxFuelBurnTime";
54 |
55 | public final ItemStackHandler inventory = new ItemStackHandler(3) {
56 | @Override
57 | public boolean isItemValid(final int slot, @Nonnull final ItemStack stack) {
58 | switch (slot) {
59 | case FUEL_SLOT:
60 | return FurnaceTileEntity.isFuel(stack);
61 | case INPUT_SLOT:
62 | return isInput(stack);
63 | case OUTPUT_SLOT:
64 | return isOutput(stack);
65 | default:
66 | return false;
67 | }
68 | }
69 |
70 | @Override
71 | protected void onContentsChanged(final int slot) {
72 | super.onContentsChanged(slot);
73 | // Mark the tile entity as having changed whenever its inventory changes.
74 | // "markDirty" tells vanilla that the chunk containing the tile entity has
75 | // changed and means the game will save the chunk to disk later.
76 | ModFurnaceTileEntity.this.markDirty();
77 | }
78 | };
79 |
80 | // Store the capability lazy optionals as fields to keep the amount of objects we use to a minimum
81 | private final LazyOptional inventoryCapabilityExternal = LazyOptional.of(() -> this.inventory);
82 | // Machines (hoppers, pipes) connected to this furnace's top can only insert/extract items from the input slot
83 | private final LazyOptional inventoryCapabilityExternalUp = LazyOptional.of(() -> new RangedWrapper(this.inventory, INPUT_SLOT, INPUT_SLOT + 1));
84 | // Machines (hoppers, pipes) connected to this furnace's bottom can only insert/extract items from the output slot
85 | private final LazyOptional inventoryCapabilityExternalDown = LazyOptional.of(() -> new RangedWrapper(this.inventory, OUTPUT_SLOT, OUTPUT_SLOT + 1));
86 | // Machines (hoppers, pipes) connected to this furnace's side can only insert/extract items from the fuel and input slots
87 | private final LazyOptional inventoryCapabilityExternalSides = LazyOptional.of(() -> new RangedWrapper(this.inventory, FUEL_SLOT, INPUT_SLOT + 1));
88 |
89 | public short smeltTimeLeft = -1;
90 | public short maxSmeltTime = -1;
91 | public short fuelBurnTimeLeft = -1;
92 | public short maxFuelBurnTime = -1;
93 | private boolean lastBurning = false;
94 |
95 | public ModFurnaceTileEntity() {
96 | super(ModTileEntityTypes.MOD_FURNACE.get());
97 | }
98 |
99 | /**
100 | * @return If the stack is not empty and has a smelting recipe associated with it
101 | */
102 | private boolean isInput(final ItemStack stack) {
103 | if (stack.isEmpty())
104 | return false;
105 | return getRecipe(stack).isPresent();
106 | }
107 |
108 | /**
109 | * @return If the stack's item is equal to the result of smelting our input
110 | */
111 | private boolean isOutput(final ItemStack stack) {
112 | final Optional result = getResult(inventory.getStackInSlot(INPUT_SLOT));
113 | return result.isPresent() && ItemStack.areItemsEqual(result.get(), stack);
114 | }
115 |
116 | /**
117 | * @return The smelting recipe for the input stack
118 | */
119 | private Optional getRecipe(final ItemStack input) {
120 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe so we make one here.
121 | return getRecipe(new Inventory(input));
122 | }
123 |
124 | /**
125 | * @return The smelting recipe for the inventory
126 | */
127 | private Optional getRecipe(final IInventory inventory) {
128 | return world.getRecipeManager().getRecipe(IRecipeType.SMELTING, inventory, world);
129 | }
130 |
131 | /**
132 | * @return The result of smelting the input stack
133 | */
134 | private Optional getResult(final ItemStack input) {
135 | // Due to vanilla's code we need to pass an IInventory into RecipeManager#getRecipe and
136 | // AbstractCookingRecipe#getCraftingResult() so we make one here.
137 | final Inventory dummyInventory = new Inventory(input);
138 | return getRecipe(dummyInventory).map(recipe -> recipe.getCraftingResult(dummyInventory));
139 | }
140 |
141 | /**
142 | * Called every tick to update our tile entity
143 | */
144 | @Override
145 | public void tick() {
146 | if (world == null || world.isRemote)
147 | return;
148 |
149 | // Fuel burning code
150 |
151 | boolean hasFuel = false;
152 | if (isBurning()) {
153 | hasFuel = true;
154 | --fuelBurnTimeLeft;
155 | }
156 |
157 | // Smelting code
158 |
159 | final ItemStack input = inventory.getStackInSlot(INPUT_SLOT).copy();
160 | final ItemStack result = getResult(input).orElse(ItemStack.EMPTY);
161 |
162 | if (!result.isEmpty() && isInput(input)) {
163 | final boolean canInsertResultIntoOutput = inventory.insertItem(OUTPUT_SLOT, result, true).isEmpty();
164 | if (canInsertResultIntoOutput) {
165 | if (!hasFuel)
166 | if (burnFuel())
167 | hasFuel = true;
168 | if (hasFuel) {
169 | if (smeltTimeLeft == -1) { // Item has not been smelted before
170 | smeltTimeLeft = maxSmeltTime = getSmeltTime(input);
171 | } else { // Item was already being smelted
172 | --smeltTimeLeft;
173 | if (smeltTimeLeft == 0) {
174 | inventory.insertItem(OUTPUT_SLOT, result, false);
175 | if (input.hasContainerItem())
176 | inventory.setStackInSlot(INPUT_SLOT, input.getContainerItem());
177 | else {
178 | input.shrink(1);
179 | inventory.setStackInSlot(INPUT_SLOT, input); // Update the data
180 | }
181 | smeltTimeLeft = -1; // Set to -1 so we smelt the next stack on the next tick
182 | }
183 | }
184 | } else // No fuel -> add to smelt time left to simulate cooling
185 | if (smeltTimeLeft < maxSmeltTime)
186 | ++smeltTimeLeft;
187 | }
188 | } else // We have an invalid input stack (somehow)
189 | smeltTimeLeft = maxSmeltTime = -1;
190 |
191 | // Syncing code
192 |
193 | // If the burning state has changed.
194 | if (lastBurning != hasFuel) { // We use hasFuel because the current fuel may be all burnt out but we have more that will be used next tick
195 |
196 | // "markDirty" tells vanilla that the chunk containing the tile entity has
197 | // changed and means the game will save the chunk to disk later.
198 | this.markDirty();
199 |
200 | final BlockState newState = this.getBlockState()
201 | .with(ModFurnaceBlock.BURNING, hasFuel);
202 |
203 | // Flag 2: Send the change to clients
204 | world.setBlockState(pos, newState, 2);
205 |
206 | // Update the last synced burning state to the current burning state
207 | lastBurning = hasFuel;
208 | }
209 |
210 | }
211 |
212 | /**
213 | * Mimics the code in {@link AbstractFurnaceTileEntity#func_214005_h()}
214 | *
215 | * @return The custom smelt time or 200 if there is no recipe for the input
216 | */
217 | private short getSmeltTime(final ItemStack input) {
218 | return getRecipe(input)
219 | .map(AbstractCookingRecipe::getCookTime)
220 | .orElse(200)
221 | .shortValue();
222 | }
223 |
224 | /**
225 | * @return If the fuel was burnt
226 | */
227 | private boolean burnFuel() {
228 | final ItemStack fuelStack = inventory.getStackInSlot(FUEL_SLOT).copy();
229 | if (!fuelStack.isEmpty()) {
230 | final int burnTime = ForgeHooks.getBurnTime(fuelStack);
231 | if (burnTime > 0) {
232 | fuelBurnTimeLeft = maxFuelBurnTime = ((short) burnTime);
233 | if (fuelStack.hasContainerItem())
234 | inventory.setStackInSlot(FUEL_SLOT, fuelStack.getContainerItem());
235 | else {
236 | fuelStack.shrink(1);
237 | inventory.setStackInSlot(FUEL_SLOT, fuelStack); // Update the data
238 | }
239 | return true;
240 | }
241 | }
242 | fuelBurnTimeLeft = maxFuelBurnTime = -1;
243 | return false;
244 | }
245 |
246 | public boolean isBurning() {
247 | return this.fuelBurnTimeLeft > 0;
248 | }
249 |
250 | /**
251 | * Retrieves the Optional handler for the capability requested on the specific side.
252 | *
253 | * @param cap The capability to check
254 | * @param side The Direction to check from. CAN BE NULL! Null is defined to represent 'internal' or 'self'
255 | * @return The requested an optional holding the requested capability.
256 | */
257 | @Nonnull
258 | @Override
259 | public LazyOptional getCapability(@Nonnull final Capability cap, @Nullable final Direction side) {
260 | if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
261 | if (side == null)
262 | return inventoryCapabilityExternal.cast();
263 | switch (side) {
264 | case DOWN:
265 | return inventoryCapabilityExternalDown.cast();
266 | case UP:
267 | return inventoryCapabilityExternalUp.cast();
268 | case NORTH:
269 | case SOUTH:
270 | case WEST:
271 | case EAST:
272 | return inventoryCapabilityExternalSides.cast();
273 | }
274 | }
275 | return super.getCapability(cap, side);
276 | }
277 |
278 | @Override
279 | public void onLoad() {
280 | super.onLoad();
281 | // We set this in onLoad instead of the constructor so that TileEntities
282 | // constructed from NBT (saved tile entities) have this set to the proper value
283 | if (world != null && !world.isRemote)
284 | lastBurning = isBurning();
285 | }
286 |
287 | /**
288 | * Read saved data from disk into the tile.
289 | */
290 | @Override
291 | public void read(final CompoundNBT compound) {
292 | super.read(compound);
293 | this.inventory.deserializeNBT(compound.getCompound(INVENTORY_TAG));
294 | this.smeltTimeLeft = compound.getShort(SMELT_TIME_LEFT_TAG);
295 | this.maxSmeltTime = compound.getShort(MAX_SMELT_TIME_TAG);
296 | this.fuelBurnTimeLeft = compound.getShort(FUEL_BURN_TIME_LEFT_TAG);
297 | this.maxFuelBurnTime = compound.getShort(MAX_FUEL_BURN_TIME_TAG);
298 | }
299 |
300 | /**
301 | * Write data from the tile into a compound tag for saving to disk.
302 | */
303 | @Nonnull
304 | @Override
305 | public CompoundNBT write(final CompoundNBT compound) {
306 | super.write(compound);
307 | compound.put(INVENTORY_TAG, this.inventory.serializeNBT());
308 | compound.putShort(SMELT_TIME_LEFT_TAG, this.smeltTimeLeft);
309 | compound.putShort(MAX_SMELT_TIME_TAG, this.maxSmeltTime);
310 | compound.putShort(FUEL_BURN_TIME_LEFT_TAG, this.fuelBurnTimeLeft);
311 | compound.putShort(MAX_FUEL_BURN_TIME_TAG, this.maxFuelBurnTime);
312 | return compound;
313 | }
314 |
315 | /**
316 | * Get an NBT compound to sync to the client with SPacketChunkData, used for initial loading of the
317 | * chunk or when many blocks change at once.
318 | * This compound comes back to you client-side in {@link #handleUpdateTag}
319 | * The default implementation ({@link TileEntity#handleUpdateTag}) calls {@link #writeInternal)}
320 | * which doesn't save any of our extra data so we override it to call {@link #write} instead
321 | */
322 | @Nonnull
323 | public CompoundNBT getUpdateTag() {
324 | return this.write(new CompoundNBT());
325 | }
326 |
327 | /**
328 | * Invalidates our tile entity
329 | */
330 | @Override
331 | public void remove() {
332 | super.remove();
333 | // We need to invalidate our capability references so that any cached references (by other mods) don't
334 | // continue to reference our capabilities and try to use them and/or prevent them from being garbage collected
335 | inventoryCapabilityExternal.invalidate();
336 | }
337 |
338 | @Nonnull
339 | @Override
340 | public ITextComponent getDisplayName() {
341 | return new TranslationTextComponent(ModBlocks.MOD_FURNACE.get().getTranslationKey());
342 | }
343 |
344 | /**
345 | * Called from {@link NetworkHooks#openGui}
346 | * (which is called from {@link ElectricFurnaceBlock#onBlockActivated} on the logical server)
347 | *
348 | * @return The logical-server-side Container for this TileEntity
349 | */
350 | @Nonnull
351 | @Override
352 | public Container createMenu(final int windowId, final PlayerInventory inventory, final PlayerEntity player) {
353 | return new ModFurnaceContainer(windowId, inventory, this);
354 | }
355 |
356 | }
357 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/mods.toml:
--------------------------------------------------------------------------------
1 | # This is an example mods.toml file. It contains the data relating to the loading mods.
2 | # There are several mandatory fields (#mandatory), and many more that are optional (#optional).
3 | # The overall format is standard TOML format, v0.5.0.
4 | # Note that there are a couple of TOML lists in this file.
5 | # Find more information on toml format here: https://github.com/toml-lang/toml
6 |
7 | # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
8 | modLoader="javafml" #mandatory
9 |
10 | # A version range to match for said mod loader - for regular FML @Mod it will be the forge version
11 | # See "https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html"
12 | # 24 is 1.13-pre, 25 is 1.13.2, 26 is 1.14.2, 27 is 1.14.3, 28 is 1.14.4, 29 is 1.15, 30 is 1.15.1, 31 is 1.15.2
13 | loaderVersion="[31,)" #mandatory
14 |
15 | # A URL to refer people to when problems occur with this mod
16 | issueTrackerURL="http://github.com/YourName/Your-Mod-Name/issues" #optional
17 |
18 | # A list of mods - how many allowed here is determined by the individual mod loader
19 | [[mods]] #mandatory
20 | # The modid of the mod
21 | modId="examplemod" #mandatory
22 | # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
23 | version="${version}" #mandatory
24 | # A display name for the mod
25 | displayName="Example Mod" #mandatory
26 | # A URL to query for updates for this mod. See the JSON update specification
27 | updateJSONURL="http://github.com/YourName/Your-Mod-Name/update.json" #optional
28 | # A URL for the "homepage" for this mod, displayed in the mod UI
29 | displayURL="http://github.com/YourName/Your-Mod-Name" #optional
30 | # A file name (in the root of the mod JAR) containing a logo for display
31 | logoFile="examplemod.png" #optional
32 | # A text field displayed in the mod UI
33 | credits="Thanks for this example mod goes to Java. The logo for this mod was taken from a post by DavidM on the Forge Forums" #optional
34 | # A text field displayed in the mod UI
35 | authors="Love, Cheese and small house plants" #optional
36 | # The description text for the mod (multi line!) (#mandatory)
37 | description='''
38 | This is a long form description of the mod. You can write whatever you want here
39 |
40 | Have some lorem ipsum.
41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis lacinia magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sagittis luctus odio eu tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque volutpat ligula eget lacus auctor sagittis. In hac habitasse platea dictumst. Nunc gravida elit vitae sem vehicula efficitur. Donec mattis ipsum et arcu lobortis, eleifend sagittis sem rutrum. Cras pharetra quam eget posuere fermentum. Sed id tincidunt justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
43 | '''
44 |
45 | # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
46 | [[dependencies.examplemod]] #optional
47 | # The modid of the dependency
48 | modId="forge" #mandatory
49 | # Does this dependency have to exist - if not, ordering below must be specified
50 | mandatory=true #mandatory
51 | # The version range of the dependency (see "https://maven.apache.org/enforcer/enforcer-rules/versionRanges.html")
52 | versionRange="[31,)" #mandatory
53 | # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory
54 | ordering="NONE"
55 | # Side this dependency is applied on - BOTH, CLIENT or SERVER
56 | side="BOTH"
57 |
58 | # Here's another dependency
59 | [[dependencies.examplemod]]
60 | modId="minecraft"
61 | mandatory=true
62 | versionRange="[1.15.2]"
63 | ordering="NONE"
64 | side="BOTH"
65 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/blockstates/electric_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "variants": {
3 | "facing=north": { "model": "examplemod:block/electric_furnace" },
4 | "facing=south": { "model": "examplemod:block/electric_furnace", "y": 180 },
5 | "facing=west": { "model": "examplemod:block/electric_furnace", "y": 270 },
6 | "facing=east": { "model": "examplemod:block/electric_furnace", "y": 90 }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/blockstates/example_block.json:
--------------------------------------------------------------------------------
1 | {
2 | "variants": {
3 | "": {
4 | "model": "examplemod:block/example_block"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/blockstates/example_ore.json:
--------------------------------------------------------------------------------
1 | {
2 | "variants": {
3 | "": {
4 | "model": "examplemod:block/example_ore"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/blockstates/heat_collector.json:
--------------------------------------------------------------------------------
1 | {
2 | "variants": {
3 | "": {
4 | "model": "examplemod:block/heat_collector"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/blockstates/mini_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "variants": {
3 | "": {
4 | "model": "examplemod:block/mini_model"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/blockstates/mod_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "variants": {
3 | "facing=north,burning=false": { "model": "examplemod:block/mod_furnace" },
4 | "facing=south,burning=false": { "model": "examplemod:block/mod_furnace", "y": 180 },
5 | "facing=west,burning=false": { "model": "examplemod:block/mod_furnace", "y": 270 },
6 | "facing=east,burning=false": { "model": "examplemod:block/mod_furnace", "y": 90 },
7 | "facing=north,burning=true": { "model": "examplemod:block/mod_furnace_burning" },
8 | "facing=south,burning=true": { "model": "examplemod:block/mod_furnace_burning", "y": 180 },
9 | "facing=west,burning=true": { "model": "examplemod:block/mod_furnace_burning", "y": 270 },
10 | "facing=east,burning=true": { "model": "examplemod:block/mod_furnace_burning", "y": 90 }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/lang/en_us.json:
--------------------------------------------------------------------------------
1 | {
2 | "block.examplemod.example_ore": "Example Ore",
3 | "block.examplemod.example_block": "Example Block",
4 | "block.examplemod.mini_model": "Mini Model",
5 | "block.examplemod.heat_collector": "Heat Collector",
6 | "block.examplemod.electric_furnace": "Electric Furnace",
7 | "block.examplemod.mod_furnace": "Mod Furnace",
8 | "itemGroup.examplemod": "Example Mod",
9 | "item.examplemod.example_crystal": "Example Crystal",
10 | "item.examplemod.wild_boar_spawn_egg": "Wild Boar Spawn Egg",
11 | "gui.examplemod.refresh_mini_model": "Refresh Mini Model",
12 | "gui.examplemod.energy": "Energy %s",
13 | "gui.examplemod.smeltTimeProgress": "Smelt time %s/%s",
14 | "gui.examplemod.fuelBurnTimeProgress": "Fuel burn time %s/%s"
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/electric_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/furnace",
3 | "textures": {
4 | "front": "examplemod:block/electric_furnace_front"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/example_block.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/cube_all",
3 | "textures": {
4 | "all": "examplemod:block/example_block"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/example_ore.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/cube_all",
3 | "textures": {
4 | "all": "examplemod:block/example_ore"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/heat_collector.json:
--------------------------------------------------------------------------------
1 | {
2 | "credit": "Made with Blockbench",
3 | "parent": "block/block",
4 | "textures": {
5 | "0": "block/anvil_top",
6 | "1": "block/anvil",
7 | "2": "block/obsidian",
8 | "particle": "block/anvil_top"
9 | },
10 | "elements": [
11 | {
12 | "from": [
13 | 11,
14 | 0,
15 | 0
16 | ],
17 | "to": [
18 | 16,
19 | 16,
20 | 5
21 | ],
22 | "faces": {
23 | "north": {
24 | "uv": [
25 | 3,
26 | 0,
27 | 13,
28 | 16
29 | ],
30 | "texture": "#0",
31 | "cullface": "north"
32 | },
33 | "east": {
34 | "uv": [
35 | 3,
36 | 0,
37 | 13,
38 | 16
39 | ],
40 | "texture": "#0",
41 | "cullface": "east"
42 | },
43 | "south": {
44 | "uv": [
45 | 3,
46 | 0,
47 | 13,
48 | 16
49 | ],
50 | "texture": "#0"
51 | },
52 | "west": {
53 | "uv": [
54 | 3,
55 | 0,
56 | 13,
57 | 16
58 | ],
59 | "texture": "#0"
60 | },
61 | "up": {
62 | "uv": [
63 | 0,
64 | 0,
65 | 16,
66 | 16
67 | ],
68 | "texture": "#1",
69 | "cullface": "up"
70 | },
71 | "down": {
72 | "uv": [
73 | 0,
74 | 0,
75 | 16,
76 | 16
77 | ],
78 | "texture": "#1",
79 | "cullface": "down"
80 | }
81 | }
82 | },
83 | {
84 | "from": [
85 | 11,
86 | 0,
87 | 11
88 | ],
89 | "to": [
90 | 16,
91 | 16,
92 | 16
93 | ],
94 | "faces": {
95 | "north": {
96 | "uv": [
97 | 3,
98 | 0,
99 | 13,
100 | 16
101 | ],
102 | "texture": "#0"
103 | },
104 | "east": {
105 | "uv": [
106 | 3,
107 | 0,
108 | 13,
109 | 16
110 | ],
111 | "texture": "#0",
112 | "cullface": "east"
113 | },
114 | "south": {
115 | "uv": [
116 | 3,
117 | 0,
118 | 13,
119 | 16
120 | ],
121 | "texture": "#0",
122 | "cullface": "south"
123 | },
124 | "west": {
125 | "uv": [
126 | 3,
127 | 0,
128 | 13,
129 | 16
130 | ],
131 | "texture": "#0"
132 | },
133 | "up": {
134 | "uv": [
135 | 0,
136 | 0,
137 | 16,
138 | 16
139 | ],
140 | "texture": "#1",
141 | "cullface": "up"
142 | },
143 | "down": {
144 | "uv": [
145 | 0,
146 | 0,
147 | 16,
148 | 16
149 | ],
150 | "texture": "#1",
151 | "cullface": "down"
152 | }
153 | }
154 | },
155 | {
156 | "from": [
157 | 0,
158 | 0,
159 | 11
160 | ],
161 | "to": [
162 | 5,
163 | 16,
164 | 16
165 | ],
166 | "faces": {
167 | "north": {
168 | "uv": [
169 | 3,
170 | 0,
171 | 13,
172 | 16
173 | ],
174 | "texture": "#0"
175 | },
176 | "east": {
177 | "uv": [
178 | 3,
179 | 0,
180 | 13,
181 | 16
182 | ],
183 | "texture": "#0"
184 | },
185 | "south": {
186 | "uv": [
187 | 3,
188 | 0,
189 | 13,
190 | 16
191 | ],
192 | "texture": "#0",
193 | "cullface": "south"
194 | },
195 | "west": {
196 | "uv": [
197 | 3,
198 | 0,
199 | 13,
200 | 16
201 | ],
202 | "texture": "#0",
203 | "cullface": "west"
204 | },
205 | "up": {
206 | "uv": [
207 | 0,
208 | 0,
209 | 16,
210 | 16
211 | ],
212 | "texture": "#1",
213 | "cullface": "up"
214 | },
215 | "down": {
216 | "uv": [
217 | 0,
218 | 0,
219 | 16,
220 | 16
221 | ],
222 | "texture": "#1",
223 | "cullface": "down"
224 | }
225 | }
226 | },
227 | {
228 | "from": [
229 | 0,
230 | 0,
231 | 0
232 | ],
233 | "to": [
234 | 5,
235 | 16,
236 | 5
237 | ],
238 | "faces": {
239 | "north": {
240 | "uv": [
241 | 3,
242 | 0,
243 | 13,
244 | 16
245 | ],
246 | "texture": "#0",
247 | "cullface": "north"
248 | },
249 | "east": {
250 | "uv": [
251 | 3,
252 | 0,
253 | 13,
254 | 16
255 | ],
256 | "texture": "#0"
257 | },
258 | "south": {
259 | "uv": [
260 | 3,
261 | 0,
262 | 13,
263 | 16
264 | ],
265 | "texture": "#0"
266 | },
267 | "west": {
268 | "uv": [
269 | 3,
270 | 0,
271 | 13,
272 | 16
273 | ],
274 | "texture": "#0",
275 | "cullface": "west"
276 | },
277 | "up": {
278 | "uv": [
279 | 0,
280 | 0,
281 | 16,
282 | 16
283 | ],
284 | "texture": "#1",
285 | "cullface": "up"
286 | },
287 | "down": {
288 | "uv": [
289 | 0,
290 | 0,
291 | 16,
292 | 16
293 | ],
294 | "texture": "#1",
295 | "cullface": "down"
296 | }
297 | }
298 | },
299 | {
300 | "from": [
301 | 5,
302 | 5,
303 | 5
304 | ],
305 | "to": [
306 | 11,
307 | 11,
308 | 11
309 | ],
310 | "faces": {
311 | "north": {
312 | "uv": [
313 | 0,
314 | 0,
315 | 16,
316 | 16
317 | ],
318 | "texture": "#2"
319 | },
320 | "east": {
321 | "uv": [
322 | 0,
323 | 0,
324 | 16,
325 | 16
326 | ],
327 | "texture": "#2"
328 | },
329 | "south": {
330 | "uv": [
331 | 0,
332 | 0,
333 | 16,
334 | 16
335 | ],
336 | "texture": "#2"
337 | },
338 | "west": {
339 | "uv": [
340 | 0,
341 | 0,
342 | 16,
343 | 16
344 | ],
345 | "texture": "#2"
346 | },
347 | "up": {
348 | "uv": [
349 | 0,
350 | 0,
351 | 16,
352 | 16
353 | ],
354 | "texture": "#2"
355 | },
356 | "down": {
357 | "uv": [
358 | 0,
359 | 0,
360 | 16,
361 | 16
362 | ],
363 | "texture": "#2"
364 | }
365 | }
366 | }
367 | ]
368 | }
369 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/mini_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/block",
3 | "textures": {
4 | "particle": "examplemod:block/mini_model"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/mod_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/furnace"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/block/mod_furnace_burning.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/furnace_on"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/electric_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "examplemod:block/electric_furnace"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/example_block.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "examplemod:block/example_block"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/example_crystal.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "item/generated",
3 | "textures": {
4 | "layer0": "examplemod:item/example_crystal"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/example_ore.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "examplemod:block/example_ore"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/heat_collector.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "examplemod:block/heat_collector"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/mini_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "block/cube_all",
3 | "textures": {
4 | "all": "examplemod:block/mini_model"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/mod_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "examplemod:block/mod_furnace"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/models/item/wild_boar_spawn_egg.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "item/template_spawn_egg"
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/block/electric_furnace_front.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/electric_furnace_front.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/block/example_block.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/example_block.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/block/example_ore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/example_ore.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/block/mini_model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/block/mini_model.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar_saddle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/entity/wild_boar/wild_boar_saddle.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/gui/container/electric_furnace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/gui/container/electric_furnace.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/gui/container/heat_collector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/gui/container/heat_collector.png
--------------------------------------------------------------------------------
/src/main/resources/assets/examplemod/textures/item/example_crystal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/assets/examplemod/textures/item/example_crystal.png
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/loot_tables/blocks/electric_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:block",
3 | "pools": [
4 | {
5 | "rolls": 1,
6 | "entries": [
7 | {
8 | "type": "minecraft:item",
9 | "name": "examplemod:electric_furnace"
10 | }
11 | ],
12 | "conditions": [
13 | {
14 | "condition": "minecraft:survives_explosion"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/loot_tables/blocks/example_block.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:block",
3 | "pools": [
4 | {
5 | "rolls": 1,
6 | "entries": [
7 | {
8 | "type": "minecraft:item",
9 | "name": "examplemod:example_block"
10 | }
11 | ],
12 | "conditions": [
13 | {
14 | "condition": "minecraft:survives_explosion"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/loot_tables/blocks/example_ore.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:block",
3 | "pools": [
4 | {
5 | "rolls": 1,
6 | "entries": [
7 | {
8 | "type": "minecraft:item",
9 | "name": "examplemod:example_ore"
10 | }
11 | ],
12 | "conditions": [
13 | {
14 | "condition": "minecraft:survives_explosion"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/loot_tables/blocks/heat_collector.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:block",
3 | "pools": [
4 | {
5 | "rolls": 1,
6 | "entries": [
7 | {
8 | "type": "minecraft:item",
9 | "name": "examplemod:heat_collector"
10 | }
11 | ],
12 | "conditions": [
13 | {
14 | "condition": "minecraft:survives_explosion"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/loot_tables/blocks/mini_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:block",
3 | "pools": [
4 | {
5 | "rolls": 1,
6 | "entries": [
7 | {
8 | "type": "minecraft:item",
9 | "name": "examplemod:mini_model"
10 | }
11 | ],
12 | "conditions": [
13 | {
14 | "condition": "minecraft:survives_explosion"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/loot_tables/blocks/mod_furnace.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:block",
3 | "pools": [
4 | {
5 | "rolls": 1,
6 | "entries": [
7 | {
8 | "type": "minecraft:item",
9 | "name": "examplemod:mod_furnace"
10 | }
11 | ],
12 | "conditions": [
13 | {
14 | "condition": "minecraft:survives_explosion"
15 | }
16 | ]
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/recipes/example_block_from_example_crystal.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:crafting_shaped",
3 | "pattern": [
4 | "###",
5 | "###",
6 | "###"
7 | ],
8 | "key": {
9 | "#": {
10 | "item": "examplemod:example_crystal"
11 | }
12 | },
13 | "result": {
14 | "item": "examplemod:example_block"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/recipes/example_crystal_from_example_block.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:crafting_shapeless",
3 | "ingredients": [
4 | {
5 | "item": "examplemod:example_block"
6 | }
7 | ],
8 | "result": {
9 | "item": "examplemod:example_crystal",
10 | "count": 9
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/recipes/example_crystal_from_smelting.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:smelting",
3 | "ingredient": {
4 | "item": "examplemod:example_ore"
5 | },
6 | "result": "examplemod:example_crystal",
7 | "experience": 0.7,
8 | "cookingtime": 200
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/recipes/heat_collector.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:crafting_shaped",
3 | "pattern": [
4 | "i i",
5 | " o ",
6 | "i i"
7 | ],
8 | "key": {
9 | "i": {
10 | "item": "minecraft:iron_block"
11 | },
12 | "o": {
13 | "item": "minecraft:obsidian"
14 | }
15 | },
16 | "result": {
17 | "item": "examplemod:heat_collector"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/resources/data/examplemod/recipes/mini_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "minecraft:crafting_shapeless",
3 | "ingredients": [
4 | {
5 | "item": "minecraft:map"
6 | },
7 | {
8 | "item": "minecraft:beacon"
9 | }
10 | ],
11 | "result": {
12 | "item": "examplemod:mini_model"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/resources/examplemod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cadiboo/Example-Mod/7e282a2b922947fc3b650e0078d706313f4afe5a/src/main/resources/examplemod.png
--------------------------------------------------------------------------------
/src/main/resources/pack.mcmeta:
--------------------------------------------------------------------------------
1 | {
2 | "pack": {
3 | "description": "examplemod resources",
4 | "pack_format": 5,
5 | "_comment": "A pack_format of 5 requires json lang files and some texture changes from 1.15. Note: Forge requires v5 pack meta for all mods."
6 | }
7 | }
8 |
--------------------------------------------------------------------------------