From c02f0c51747c80916fe490550da8655fb74295fc Mon Sep 17 00:00:00 2001 From: Gio Date: Wed, 28 Jan 2026 14:56:03 +0100 Subject: [PATCH 1/4] feat: refactor reactor heat management with service interfaces and dependency injection --- .../ReactorControllerBlockEntity.java | 243 ++++++++++-------- .../service/DefaultHeatService.java | 28 ++ .../service/DefaultPersistenceService.java | 43 ++++ .../controller/service/IHeatService.java | 10 + .../service/IPersistenceService.java | 9 + .../reactorLogic/DefaultHeatCalculator.java | 78 ++++++ .../DefaultOverheatController.java | 39 +++ .../multiblock/reactorLogic/HeatManager.java | 123 ++------- .../reactorLogic/IHeatCalculator.java | 8 + .../reactorLogic/IOverheatController.java | 8 + 10 files changed, 380 insertions(+), 209 deletions(-) create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultHeatService.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IHeatService.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IPersistenceService.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultHeatCalculator.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultOverheatController.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IHeatCalculator.java create mode 100644 src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IOverheatController.java diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java index 45ba7cfa..a572cf9b 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlockEntity.java @@ -37,6 +37,10 @@ import net.nuclearteam.createnuclear.foundation.utility.CreateNuclearLang; import net.nuclearteam.createnuclear.content.multiblock.pattern.ReactorPattern; import net.nuclearteam.createnuclear.content.multiblock.reactorLogic.HeatManager; +import net.nuclearteam.createnuclear.content.multiblock.controller.service.IHeatService; +import net.nuclearteam.createnuclear.content.multiblock.controller.service.DefaultHeatService; +import net.nuclearteam.createnuclear.content.multiblock.controller.service.IPersistenceService; +import net.nuclearteam.createnuclear.content.multiblock.controller.service.DefaultPersistenceService; import java.util.ArrayList; @@ -50,47 +54,96 @@ public class ReactorControllerBlockEntity extends SmartBlockEntity implements II /** The assembled state is stored in the block state (`ReactorControllerBlock.ASSEMBLED`). * Use the helper accessors below to query or toggle it to keep entity/blockstate consistent. */ - public int speed = 16; // This is the result speed of the reactor, change this to change the total capacity + // configurable public surface reduced; fields are private and accessible via getters/setters + private int speed = 16; // This is the result speed of the reactor, change this to change the total capacity - public ReactorControllerBlock controller; - protected ReactorPattern pattern = new ReactorPattern(); - public ReactorControllerInventory inventory; - public int countUraniumRod; - public int countGraphiteRod; - public int heat; + private ReactorControllerBlock controller; + private final ReactorPattern pattern = new ReactorPattern(); + private final ReactorControllerInventory inventory; + private int countUraniumRod; + private int countGraphiteRod; + private int heat; - public double total; - public CompoundTag screen_pattern = new CompoundTag(); - public ItemStack configuredPattern; + private double total; + private CompoundTag screen_pattern = new CompoundTag(); + private ItemStack configuredPattern; private BigItemStack bigFuelItem; private BigItemStack bigCoolerItem; private List bigFluidStack; - public int reactorSize = 0; - public String reactorFacing = "null"; + private int reactorSize = 0; + private String reactorFacing = "null"; // les pos sont [xMin, xMax, yMin, yMax, zMin, zMax] - public int[] reactorPos; + private int[] reactorPos; private boolean needsToResolveEntities = false; private final ReactorInputManagerI inputManager; private final ReactorOutputManagerI outputManager; private final ReactorInputFluidManagerI inputFluidManager; - HeatManager heatManager = new HeatManager(); + // services (dependencies) - abstracted behind interfaces to follow DIP + private final IHeatService heatService; + private final IPersistenceService persistenceService; + // service fields are injected; implementations live in separate classes + + // --- Accessors used by external services (persistence) --- + public ReactorControllerInventory getInventoryObject() { return this.inventory; } + public void deserializeInventory(CompoundTag tag) { this.inventory.deserializeNBT(tag); } + public CompoundTag serializeInventory() { return this.inventory.serializeNBT(); } + + public ItemStack getConfiguredPattern() { return this.configuredPattern; } + public void setConfiguredPattern(ItemStack stack) { this.configuredPattern = stack; } + + public BigItemStack getBigFuelItem() { return this.bigFuelItem; } + public void setBigFuelItem(BigItemStack b) { this.bigFuelItem = b; } + public BigItemStack getBigCoolerItem() { return this.bigCoolerItem; } + public void setBigCoolerItem(BigItemStack b) { this.bigCoolerItem = b; } + + public int getReactorSize() { return this.reactorSize; } + public void setReactorSize(int s) { this.reactorSize = s; } + + public String getReactorFacing() { return this.reactorFacing; } + public void setReactorFacing(String f) { this.reactorFacing = f; } + + public int[] getReactorPos() { return this.reactorPos; } + public void setReactorPos(int[] p) { this.reactorPos = p; } + + public double getTotal() { return this.total; } + public void setTotal(double t) { this.total = t; } + + /** Default constructor used by the game; delegates to main constructor with default implementations. */ public ReactorControllerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + this(type, pos, state, + new ReactorInputManager(), + new ReactorOutputManager(), + new ReactorInputFluidManager(), + new DefaultHeatService(new HeatManager()), + new DefaultPersistenceService()); + } + + /** Main constructor allowing dependency injection for testability and DIP compliance. */ + public ReactorControllerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, + ReactorInputManagerI inputManager, + ReactorOutputManagerI outputManager, + ReactorInputFluidManagerI inputFluidManager, + IHeatService heatService, + IPersistenceService persistenceService) { super(type, pos, state); - inventory = new ReactorControllerInventory(this); - configuredPattern = ItemStack.EMPTY; + this.inventory = new ReactorControllerInventory(this); + this.configuredPattern = ItemStack.EMPTY; + + this.inputManager = inputManager; + this.outputManager = outputManager; + this.inputFluidManager = inputFluidManager; - inputManager = new ReactorInputManager(); - outputManager = new ReactorOutputManager(); - inputFluidManager = new ReactorInputFluidManager(); + this.bigFuelItem = new BigItemStack(ItemStack.EMPTY); + this.bigCoolerItem = new BigItemStack(ItemStack.EMPTY); + this.bigFluidStack = new ArrayList<>(); - bigFuelItem = new BigItemStack(ItemStack.EMPTY); - bigCoolerItem = new BigItemStack(ItemStack.EMPTY); - bigFluidStack = new ArrayList<>(); + this.heatService = heatService; + this.persistenceService = persistenceService; } @Override @@ -147,27 +200,12 @@ public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneak @Override protected void read(CompoundTag compound, boolean clientPacket) { super.read(compound, clientPacket); // Toujours en premier pour les coordonnées de base - - // Lecture des Inputs + // delegate managers and persistence this.inputManager.read(compound); this.outputManager.read(compound); this.inputFluidManager.read(compound); - // 1. Tes nouvelles variables simples - this.reactorSize = compound.getInt("reactorSize"); - this.reactorFacing = compound.getString("reactorFacing"); - this.reactorPos = compound.getIntArray("reactorPose"); - this.total = compound.getDouble("total"); - - // 2. Gestion des items - if (!clientPacket) { - inventory.deserializeNBT(compound.getCompound("pattern")); - } - configuredPattern = ItemStack.of(compound.getCompound("items")); - - bigFuelItem = BigItemStack.read(compound.getCompound("bigFuel")); - bigCoolerItem = BigItemStack.read(compound.getCompound("bigCooler")); - + this.persistenceService.readBasicState(this, compound, clientPacket); this.needsToResolveEntities = true; } @@ -178,23 +216,7 @@ protected void write(CompoundTag compound, boolean clientPacket) { this.outputManager.write(compound); this.inputFluidManager.write(compound); - // 1. Tes nouvelles variables simples - compound.putInt("reactorSize", this.reactorSize); - compound.putString("reactorFacing", this.reactorFacing); - if (this.reactorPos != null) { - compound.putIntArray("reactorPose", this.reactorPos); - } - - compound.putDouble("total", calculateProgress()); - - // 2. Gestion des items (Ton code existant) - if (!clientPacket) { - compound.put("pattern", inventory.serializeNBT()); - } - compound.put("items", configuredPattern.serializeNBT()); - - compound.put("bigFuel", bigFuelItem.write()); - compound.put("bigCooler", bigCoolerItem.write()); + this.persistenceService.writeBasicState(this, compound, clientPacket); } @@ -218,8 +240,8 @@ public double calculateProgress() { countGraphiteRod = configuredPattern.getOrCreateTag().getInt("countGraphiteRod"); countUraniumRod = configuredPattern.getOrCreateTag().getInt("countUraniumRod"); - double totalGraphiteRodLife = (double) heatManager.graphiteTimer / countGraphiteRod; - double totalUraniumRodLife = (double) heatManager.uraniumTimer / countUraniumRod; + double totalGraphiteRodLife = (double) heatService.getGraphiteTimer() / Math.max(1, countGraphiteRod); + double totalUraniumRodLife = (double) heatService.getUraniumTimer() / Math.max(1, countUraniumRod); return totalGraphiteRodLife + totalUraniumRodLife; } @@ -256,66 +278,77 @@ public void tick() { super.tick(); if (level.isClientSide) return; - int heat = (int) configuredPattern.getOrCreateTag().getDouble("heat"); countGraphiteRod = configuredPattern.getOrCreateTag().getInt("countGraphiteRod"); countUraniumRod = configuredPattern.getOrCreateTag().getInt("countUraniumRod"); - if (needsToResolveEntities) { - List handlers = inputManager.getItemHandlers(level); - CreateNuclear.LOGGER.warn("Resolving inputs after load, handlers found: {}", handlers.size()); - needsToResolveEntities = false; + + resolveEntitiesIfNeeded(); + + if (!isAssembled()) return; + + // gather IO snapshot + VirtualReactorInputsItem virtualReactorInputsItem = inputManager.getInventory(level); + VirtualReactorInputFluid virtualReactorInputFluid = inputFluidManager.getInventory(level); + this.bigFuelItem = virtualReactorInputsItem.getBigFuelRod(); + this.bigCoolerItem = virtualReactorInputsItem.getBigCooledRod(); + this.bigFluidStack = VirtualReactorInputFluid.toBigList(virtualReactorInputFluid.fluids()); + + handleAssembledState(heat); + } + + // --- extracted sub-steps to keep single responsibility per method --- + private void resolveEntitiesIfNeeded() { + if (!needsToResolveEntities) return; + List handlers = inputManager.getItemHandlers(level); + CreateNuclear.LOGGER.warn("Resolving inputs after load, handlers found: {}", handlers.size()); + needsToResolveEntities = false; + this.setChanged(); + } + + private void handleAssembledState(int heat) { + if (!isReadyToRun()) { + updateHeatOnly(); + if (!this.outputManager.getBlocksPosition().isEmpty()) rotate(getBlockState(), getLevel(), 0); this.setChanged(); + this.notifyUpdate(); + return; } - if (isAssembled()) { - List handlers = inputManager.getItemHandlers(level); - List fluidHandlers = inputFluidManager.getFuildHandlers(level); - VirtualReactorInputsItem virtualReactorInputsItem = inputManager.getInventory(level); - VirtualReactorInputFluid virtualReactorInputFluid = inputFluidManager.getInventory(level); - bigFuelItem = virtualReactorInputsItem.getBigFuelRod(); - bigCoolerItem = virtualReactorInputsItem.getBigCooledRod(); - bigFluidStack = VirtualReactorInputFluid.toBigList(virtualReactorInputFluid.fluids()); - - if (!isEmptyConfiguredPattern() && bigFuelItem.count > 0 && bigCoolerItem.count > 0) { - if (this.inputManager.size() > 0) { - this.setChanged(); - this.notifyUpdate(); - configuredPattern.getOrCreateTag().putDouble("heat", heatManager.calculateHeat(bigFuelItem, bigCoolerItem, countGraphiteRod, countUraniumRod, inventory)); - if (!this.outputManager.getBlocksPosition().isEmpty()) { - rotate(getBlockState(), getLevel(), heat); - } - if (updateTimers()) { - boolean extracted = inputManager.extractItems(level, 1, 1); - if (extracted) { - this.setChanged(); - this.notifyUpdate(); - total = calculateProgress(); - - if (IHeat.HeatLevel.isNotDanger(heat)) { - //... - } else { - EventTriggerPacket packet = new EventTriggerPacket(600); - CreateNuclear.LOGGER.warn("hum EventTriggerBlock ? {}", packet); - CNPackets.sendToNear(level, getBlockPos(), 32, packet); - } - return; - } - } + // ready to run + this.setChanged(); + this.notifyUpdate(); - this.setChanged(); - this.notifyUpdate(); - } - } else { - configuredPattern.getOrCreateTag().putDouble("heat", heatManager.calculateHeat(bigFuelItem, bigCoolerItem, countGraphiteRod, countUraniumRod, inventory)); - if (!this.outputManager.getBlocksPosition().isEmpty()) { - rotate(getBlockState(), getLevel(), 0); - } + configuredPattern.getOrCreateTag().putDouble("heat", heatService.calculateHeat(bigFuelItem, bigCoolerItem, countGraphiteRod, countUraniumRod, inventory)); + if (!this.outputManager.getBlocksPosition().isEmpty()) { + rotate(getBlockState(), getLevel(), heat); + } + + if (updateTimers()) { + boolean extracted = inputManager.extractItems(level, 1, 1); + if (extracted) { this.setChanged(); this.notifyUpdate(); + total = calculateProgress(); + + if (IHeat.HeatLevel.isNotDanger(heat)) { + // normal + } else { + EventTriggerPacket packet = new EventTriggerPacket(600); + CreateNuclear.LOGGER.warn("hum EventTriggerBlock ? {}", packet); + CNPackets.sendToNear(level, getBlockPos(), 32, packet); + } } } } + private boolean isReadyToRun() { + return !isEmptyConfiguredPattern() && bigFuelItem.count > 0 && bigCoolerItem.count > 0 && this.inputManager.size() > 0; + } + + private void updateHeatOnly() { + configuredPattern.getOrCreateTag().putDouble("heat", heatService.calculateHeat(bigFuelItem, bigCoolerItem, countGraphiteRod, countUraniumRod, inventory)); + } + private boolean isEmptyConfiguredPattern() { return configuredPattern.isEmpty() || configuredPattern.getOrCreateTag().isEmpty(); } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultHeatService.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultHeatService.java new file mode 100644 index 00000000..6b16b0cb --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultHeatService.java @@ -0,0 +1,28 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.service; + +import com.simibubi.create.content.logistics.BigItemStack; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerInventory; +import net.nuclearteam.createnuclear.content.multiblock.reactorLogic.HeatManager; + +public class DefaultHeatService implements IHeatService { + private final HeatManager impl; + + public DefaultHeatService(HeatManager impl) { + this.impl = impl; + } + + @Override + public int getGraphiteTimer() { + return impl.getGraphiteTimer(); + } + + @Override + public int getUraniumTimer() { + return impl.getUraniumTimer(); + } + + @Override + public double calculateHeat(BigItemStack fuel, BigItemStack cooler, int graphiteCount, int uraniumCount, ReactorControllerInventory inventory) { + return impl.calculateHeat(fuel, cooler, graphiteCount, uraniumCount, inventory); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java new file mode 100644 index 00000000..423eda01 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java @@ -0,0 +1,43 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.service; + +import com.simibubi.create.content.logistics.BigItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; + +public class DefaultPersistenceService implements IPersistenceService { + @Override + public void readBasicState(ReactorControllerBlockEntity owner, CompoundTag compound, boolean clientPacket) { + owner.setReactorSize(compound.getInt("reactorSize")); + owner.setReactorFacing(compound.getString("reactorFacing")); + owner.setReactorPos(compound.getIntArray("reactorPose")); + owner.setTotal(compound.getDouble("total")); + + if (!clientPacket) { + owner.deserializeInventory(compound.getCompound("pattern")); + } + owner.setConfiguredPattern(ItemStack.of(compound.getCompound("items"))); + + owner.setBigFuelItem(BigItemStack.read(compound.getCompound("bigFuel"))); + owner.setBigCoolerItem(BigItemStack.read(compound.getCompound("bigCooler"))); + } + + @Override + public void writeBasicState(ReactorControllerBlockEntity owner, CompoundTag compound, boolean clientPacket) { + compound.putInt("reactorSize", owner.getReactorSize()); + compound.putString("reactorFacing", owner.getReactorFacing()); + if (owner.getReactorPos() != null) { + compound.putIntArray("reactorPose", owner.getReactorPos()); + } + + compound.putDouble("total", owner.calculateProgress()); + + if (!clientPacket) { + compound.put("pattern", owner.serializeInventory()); + } + compound.put("items", owner.getConfiguredPattern().serializeNBT()); + + compound.put("bigFuel", owner.getBigFuelItem().write()); + compound.put("bigCooler", owner.getBigCoolerItem().write()); + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IHeatService.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IHeatService.java new file mode 100644 index 00000000..68c252bf --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IHeatService.java @@ -0,0 +1,10 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.service; + +import com.simibubi.create.content.logistics.BigItemStack; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerInventory; + +public interface IHeatService { + int getGraphiteTimer(); + int getUraniumTimer(); + double calculateHeat(BigItemStack fuel, BigItemStack cooler, int graphiteCount, int uraniumCount, ReactorControllerInventory inventory); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IPersistenceService.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IPersistenceService.java new file mode 100644 index 00000000..98fe4c12 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/IPersistenceService.java @@ -0,0 +1,9 @@ +package net.nuclearteam.createnuclear.content.multiblock.controller.service; + +import net.minecraft.nbt.CompoundTag; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerBlockEntity; + +public interface IPersistenceService { + void readBasicState(ReactorControllerBlockEntity owner, CompoundTag compound, boolean clientPacket); + void writeBasicState(ReactorControllerBlockEntity owner, CompoundTag compound, boolean clientPacket); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultHeatCalculator.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultHeatCalculator.java new file mode 100644 index 00000000..55cc5fbb --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultHeatCalculator.java @@ -0,0 +1,78 @@ +package net.nuclearteam.createnuclear.content.multiblock.reactorLogic; + +import com.simibubi.create.content.logistics.BigItemStack; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; +import net.nuclearteam.createnuclear.CNTags; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerInventory; + +public class DefaultHeatCalculator implements IHeatCalculator { + private final int[][] formattedPattern = new int[][]{ + {99,99,99,0,1,2,99,99,99}, + {99,99,3,4,5,6,7,99,99}, + {99,8,9,10,11,12,13,14,99}, + {15,16,17,18,19,20,21,22,23}, + {24,25,26,27,28,29,30,31,32}, + {33,34,35,36,37,38,39,40,41}, + {99,42,43,44,45,46,47,48,99}, + {99,99,49,50,51,52,53,99,99}, + {99,99,99,54,55,56,99,99,99} + }; + private final int[][] offsets = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; + + // configuration constants (can be refactored to config object if needed) + private final int baseUraniumHeat = 25; + private final int baseGraphiteHeat = -10; + private final int proximityUraniumHeat = 5; + private final int proximityGraphiteHeat = -5; + + @Override + public double computeHeat(BigItemStack bigFuelItem, BigItemStack bigCoolerItem, int countGraphiteRod, int countUraniumRod, ReactorControllerInventory inventory, double overHeat) { + int heat = 0; + + ListTag list = inventory.getStackInSlot(0).getOrCreateTag().getCompound("pattern").getList("Items", Tag.TAG_COMPOUND); + + for (int i = 0; i < list.size(); i++) { + ItemStack currentStack = ItemStack.of(list.getCompound(i)); + String currentRod = ""; + if (currentStack.is(CNTags.CNItemTags.FUEL.tag)) { + heat += baseUraniumHeat; + currentRod = "u"; + } else if (currentStack.is(CNTags.CNItemTags.COOLER.tag)) { + heat += baseGraphiteHeat; + currentRod = "g"; + } + + // find position in formattedPattern and check neighbors + for (int j = 0; j < formattedPattern.length; j++) { + for (int k = 0; k < formattedPattern[j].length; k++) { + if (formattedPattern[j][k] == 99) continue; + if (list.getCompound(i).getInt("Slot") != formattedPattern[j][k]) continue; + + for (int[] offset : offsets) { + int nj = j + offset[0]; + int nk = k + offset[1]; + if (nj < 0 || nj >= formattedPattern.length || nk < 0 || nk >= formattedPattern[j].length) continue; + + int neighborSlot = formattedPattern[nj][nk]; + for (int l = 0; l < list.size(); l++) { + if (list.getCompound(l).getInt("Slot") == neighborSlot) { + if ("u".equals(currentRod)) { + ItemStack stack = ItemStack.of(list.getCompound(l)); + if (stack.is(CNTags.CNItemTags.FUEL.tag)) { + heat += proximityUraniumHeat; + } else if (stack.is(CNTags.CNItemTags.COOLER.tag)) { + heat += proximityGraphiteHeat; + } + } + } + } + } + } + } + } + + return heat + overHeat; + } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultOverheatController.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultOverheatController.java new file mode 100644 index 00000000..b66676ad --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/DefaultOverheatController.java @@ -0,0 +1,39 @@ +package net.nuclearteam.createnuclear.content.multiblock.reactorLogic; + +class DefaultOverheatController implements IOverheatController { + private int overFlowHeatTimer = 0; + private int overFlowLimiter = 30; + private double overHeat = 0; + + private final int maxUraniumPerGraphite = 3; + private final int graphiteTimer = 3600; + private final int uraniumTimer = 3600; + + @Override + public void updateState(int countGraphiteRod, int countUraniumRod) { + if (countGraphiteRod <= 0) return; + + if (countUraniumRod > countGraphiteRod * maxUraniumPerGraphite) { + overFlowHeatTimer++; + if (overFlowHeatTimer >= overFlowLimiter) { + overHeat += 1; + overFlowHeatTimer = 0; + if (overFlowLimiter > 2) overFlowLimiter -= 1; + } + } else { + overFlowHeatTimer = 0; + overFlowLimiter = 30; + if (overHeat > 0) overHeat -= 2; + else overHeat = 0; + } + } + + @Override + public double getOverHeat() { return overHeat; } + + @Override + public int getGraphiteTimer() { return graphiteTimer; } + + @Override + public int getUraniumTimer() { return uraniumTimer; } +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/HeatManager.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/HeatManager.java index 28bc36ed..21129a7c 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/HeatManager.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/HeatManager.java @@ -1,118 +1,33 @@ package net.nuclearteam.createnuclear.content.multiblock.reactorLogic; import com.simibubi.create.content.logistics.BigItemStack; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.world.item.ItemStack; -import net.nuclearteam.createnuclear.CNTags; -import net.nuclearteam.createnuclear.CreateNuclear; import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerInventory; +/** + * HeatManager facade delegating to extracted components (in separate files). + */ public class HeatManager { - public int heat; - int overFlowHeatTimer = 0; - int overFlowLimiter = 30; - double overHeat = 0; - public int baseUraniumHeat = 25; - public int baseGraphiteHeat = -10; - public int proximityUraniumHeat = 5; - public int proximityGraphiteHeat = -5; - public int maxUraniumPerGraphite = 3; - public int graphiteTimer = 3600; - public int uraniumTimer = 3600; + private final IHeatCalculator calculator; + private final IOverheatController overheatController; - private final int[][] formattedPattern = new int[][]{ - {99,99,99,0,1,2,99,99,99}, - {99,99,3,4,5,6,7,99,99}, - {99,8,9,10,11,12,13,14,99}, - {15,16,17,18,19,20,21,22,23}, - {24,25,26,27,28,29,30,31,32}, - {33,34,35,36,37,38,39,40,41}, - {99,42,43,44,45,46,47,48,99}, - {99,99,49,50,51,52,53,99,99}, - {99,99,99,54,55,56,99,99,99} - }; - private final int[][] offsets = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; - - public double calculateHeat(BigItemStack bigFuelItem, BigItemStack bigCoolerItem, int countGraphiteRod, int countUraniumRod, ReactorControllerInventory inventory) { - heat = 0; - - if (bigFuelItem.count <= 0 || bigCoolerItem.count <= 0) { - return 0; - } - - updateOverheatState(countGraphiteRod, countUraniumRod); - - // the offsets for the four directions (down, up, right, left) is int[][] offsets = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; (defined at the top of the class) - String currentRod = ""; - ListTag list = inventory.getStackInSlot(0).getOrCreateTag().getCompound("pattern").getList("Items", Tag.TAG_COMPOUND); - for (int i = 0; i < list.size(); i++) { - if (ItemStack.of(list.getCompound(i)).is(CNTags.CNItemTags.FUEL.tag)) { - heat += baseUraniumHeat; - currentRod = "u"; - } else if (ItemStack.of(list.getCompound(i)).is(CNTags.CNItemTags.COOLER.tag)) { - heat += baseGraphiteHeat; - currentRod = "g"; - } - for (int j = 0; j < formattedPattern.length; j++) { - for (int k = 0; k < formattedPattern[j].length; k++) { - // Skip if the current pattern value is 99 - if (formattedPattern[j][k] == 99) continue; - - // Check if the current slot matches the pattern - if (list.getCompound(i).getInt("Slot") != formattedPattern[j][k]) continue; + public HeatManager(IHeatCalculator calculator, IOverheatController overheatController) { + this.calculator = calculator; + this.overheatController = overheatController; + } - // For each neighbor (up, down, right, left) - for (int[] offset : offsets) { - int nj = j + offset[0]; - int nk = k + offset[1]; + public HeatManager() { + this(new DefaultHeatCalculator(), new DefaultOverheatController()); + } - // Check if the indices are within the array boundaries - if (nj < 0 || nj >= formattedPattern.length || nk < 0 || nk >= formattedPattern[j].length) - continue; + public double calculateHeat(BigItemStack bigFuelItem, BigItemStack bigCoolerItem, int countGraphiteRod, int countUraniumRod, ReactorControllerInventory inventory) { + if (bigFuelItem == null || bigCoolerItem == null) return 0; + if (bigFuelItem.count <= 0 || bigCoolerItem.count <= 0) return 0; - int neighborSlot = formattedPattern[nj][nk]; + overheatController.updateState(countGraphiteRod, countUraniumRod); - // Loop through the list to find the neighbor slot - for (int l = 0; l < list.size(); l++) { - if (list.getCompound(l).getInt("Slot") == neighborSlot) { - // If the currentRod equals "u", apply the corresponding heat - if (currentRod.equals("u")) { - ItemStack stack = ItemStack.of(list.getCompound(i)); - if (stack.is(CNTags.CNItemTags.FUEL.tag)) { - heat += proximityUraniumHeat; - } else if (stack.is(CNTags.CNItemTags.COOLER.tag)) { - heat += proximityGraphiteHeat; - } - } - } - } - } - } - } - } - return heat + overHeat; + return calculator.computeHeat(bigFuelItem, bigCoolerItem, countGraphiteRod, countUraniumRod, inventory, overheatController.getOverHeat()); } - private void updateOverheatState(int countGraphiteRod, int countUraniumRod) { - // if more than maxUraniumPerGraphite of the rods are uranium, the reactor will overheat - if (countUraniumRod > countGraphiteRod * maxUraniumPerGraphite) { - overFlowHeatTimer++; - if (overFlowHeatTimer >= overFlowLimiter) { - overHeat+=1; - overFlowHeatTimer= 0; - if (overFlowLimiter > 2) { - overFlowLimiter -= 1; - } - } - } else { - overFlowHeatTimer = 0; - overFlowLimiter = 30; - if (overHeat > 0) { - overHeat -= 2; - } else { - overHeat = 0; - } - } - } + public int getGraphiteTimer() { return overheatController.getGraphiteTimer(); } + public int getUraniumTimer() { return overheatController.getUraniumTimer(); } } diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IHeatCalculator.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IHeatCalculator.java new file mode 100644 index 00000000..4e52ed40 --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IHeatCalculator.java @@ -0,0 +1,8 @@ +package net.nuclearteam.createnuclear.content.multiblock.reactorLogic; + +import com.simibubi.create.content.logistics.BigItemStack; +import net.nuclearteam.createnuclear.content.multiblock.controller.ReactorControllerInventory; + +public interface IHeatCalculator { + double computeHeat(BigItemStack bigFuelItem, BigItemStack bigCoolerItem, int countGraphiteRod, int countUraniumRod, ReactorControllerInventory inventory, double overHeat); +} diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IOverheatController.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IOverheatController.java new file mode 100644 index 00000000..efb8bc7f --- /dev/null +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/reactorLogic/IOverheatController.java @@ -0,0 +1,8 @@ +package net.nuclearteam.createnuclear.content.multiblock.reactorLogic; + +public interface IOverheatController { + void updateState(int countGraphiteRod, int countUraniumRod); + double getOverHeat(); + int getGraphiteTimer(); + int getUraniumTimer(); +} From 06b151dbb9a39b9688248a05d45ce0e05c019953 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Sun, 1 Feb 2026 10:07:05 +0100 Subject: [PATCH 2/4] refactor: update reactor state methods to use multiblock terminology --- .../service/DefaultPersistenceService.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java index 423eda01..41b3ccfe 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/service/DefaultPersistenceService.java @@ -8,9 +8,9 @@ public class DefaultPersistenceService implements IPersistenceService { @Override public void readBasicState(ReactorControllerBlockEntity owner, CompoundTag compound, boolean clientPacket) { - owner.setReactorSize(compound.getInt("reactorSize")); - owner.setReactorFacing(compound.getString("reactorFacing")); - owner.setReactorPos(compound.getIntArray("reactorPose")); + owner.setMultiblockSize(compound.getInt("reactorSize")); + owner.setMultiblockFacing(compound.getString("reactorFacing")); + owner.setMultiblockStructure(compound.getIntArray("reactorPose")); owner.setTotal(compound.getDouble("total")); if (!clientPacket) { @@ -24,10 +24,10 @@ public void readBasicState(ReactorControllerBlockEntity owner, CompoundTag compo @Override public void writeBasicState(ReactorControllerBlockEntity owner, CompoundTag compound, boolean clientPacket) { - compound.putInt("reactorSize", owner.getReactorSize()); - compound.putString("reactorFacing", owner.getReactorFacing()); - if (owner.getReactorPos() != null) { - compound.putIntArray("reactorPose", owner.getReactorPos()); + compound.putInt("reactorSize", owner.getMultiblockSize()); + compound.putString("reactorFacing", owner.getMultiblockFacing()); + if (owner.getMultiblockPos() != null) { + compound.putIntArray("reactorPose", owner.getMultiblockPos()); } compound.putDouble("total", owner.calculateProgress()); From aad4ada7662b0c318c89e893487b367032371816 Mon Sep 17 00:00:00 2001 From: Giovanni Date: Sun, 1 Feb 2026 10:07:12 +0100 Subject: [PATCH 3/4] refactor: update reactor controller methods to use multiblock terminology --- .../controller/ReactorControllerBlock.java | 26 ++++++------ .../ReactorControllerBlockEntity.java | 42 +++++++------------ 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java index 32e039f7..5d7787c5 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/controller/ReactorControllerBlock.java @@ -85,22 +85,22 @@ public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Play } else { - if (heldItem.is(CNItems.REACTOR_BLUEPRINT.get()) && controllerBlockEntity.inventory.getItem(0).isEmpty()){ + if (heldItem.is(CNItems.REACTOR_BLUEPRINT.get()) && controllerBlockEntity.getInventoryObject().getItem(0).isEmpty()){ withBlockEntityDo(worldIn, pos, be -> { - be.inventory.setStackInSlot(0, heldItem); - be.configuredPattern = heldItem; + be.getInventoryObject().setStackInSlot(0, heldItem); + be.setConfiguredPattern(heldItem); player.setItemInHand(handIn, ItemStack.EMPTY); }); return InteractionResult.SUCCESS; } - else if (heldItem.isEmpty() && !controllerBlockEntity.inventory.getItem(0).isEmpty()) { + else if (heldItem.isEmpty() && !controllerBlockEntity.getInventoryObject().getItem(0).isEmpty()) { withBlockEntityDo(worldIn, pos, be -> { - player.setItemInHand(handIn, be.inventory.getItem(0)); - be.inventory.setStackInSlot(0, ItemStack.EMPTY); - be.configuredPattern = ItemStack.EMPTY; - be.total = 0.0; + player.setItemInHand(handIn, be.getInventoryObject().getItem(0)); + be.getInventoryObject().setStackInSlot(0, ItemStack.EMPTY); + be.setConfiguredPattern(ItemStack.EMPTY); + be.setTotal(0.0); be.rotate(be.getBlockState(), be.getLevel(), 0); be.notifyUpdate(); }); @@ -108,7 +108,7 @@ else if (heldItem.isEmpty() && !controllerBlockEntity.inventory.getItem(0).isEmp return InteractionResult.SUCCESS; } - else if (!heldItem.isEmpty() && !controllerBlockEntity.inventory.getItem(0).isEmpty()) { + else if (!heldItem.isEmpty() && !controllerBlockEntity.getInventoryObject().getItem(0).isEmpty()) { return InteractionResult.PASS; } } @@ -120,7 +120,7 @@ public void onRemove(BlockState state, Level worldIn, BlockPos pos, BlockState n if (!state.hasBlockEntity() || state.getBlock() == newState.getBlock()) return; - withBlockEntityDo(worldIn, pos, be -> ItemHelper.dropContents(worldIn, pos, be.inventory)); + withBlockEntityDo(worldIn, pos, be -> ItemHelper.dropContents(worldIn, pos, be.getInventoryObject())); worldIn.removeBlockEntity(pos); ReactorControllerBlock controller = (ReactorControllerBlock) state.getBlock(); @@ -173,13 +173,13 @@ public void Verify(BlockState state, BlockPos pos, Level level, List type, BlockPos pos, BlockState state) { - this(type, pos, state, - new ReactorInputManager(), - new ReactorOutputManager(), - new ReactorInputFluidManager(), - new DefaultHeatService(new HeatManager()), - new DefaultPersistenceService()); - } - /** Main constructor allowing dependency injection for testability and DIP compliance. */ - public ReactorControllerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state, - ReactorInputManagerI inputManager, - ReactorOutputManagerI outputManager, - ReactorInputFluidManagerI inputFluidManager, - IHeatService heatService, - IPersistenceService persistenceService) { + public ReactorControllerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); this.inventory = new ReactorControllerInventory(this); this.configuredPattern = ItemStack.EMPTY; - this.inputManager = inputManager; - this.outputManager = outputManager; - this.inputFluidManager = inputFluidManager; + this.inputManager = new ReactorInputManager(); + this.outputManager = new ReactorOutputManager(); + this.inputFluidManager = new ReactorInputFluidManager(); this.bigFuelItem = new BigItemStack(ItemStack.EMPTY); this.bigCoolerItem = new BigItemStack(ItemStack.EMPTY); this.bigFluidStack = new ArrayList<>(); - this.heatService = heatService; - this.persistenceService = persistenceService; + this.heatService = new DefaultHeatService(new HeatManager()); + this.persistenceService = new DefaultPersistenceService(); } @Override @@ -418,6 +404,7 @@ public void rotate(BlockState state, Level level, int rotation) { } } + @Deprecated public int[] getStructureBounds(BlockPos startPos, int structureSize, String facing) { int[] northOffsets5x5 = new int[] {-2, 2, -3, 3, 0, 4}; int[] northOffsets7x7 = new int[] {-3, 3, -4, 4, 0, 6}; @@ -464,6 +451,7 @@ public int[] getStructureBounds(BlockPos startPos, int structureSize, String fac } } + @Deprecated private int[] applyOffset(BlockPos pos, int[] offset) { int x = pos.getX(); int y = pos.getY(); From 46bed38d8d013fb9adc4a14219ba148ff5f54dfd Mon Sep 17 00:00:00 2001 From: Giovanni Date: Sun, 1 Feb 2026 10:07:27 +0100 Subject: [PATCH 4/4] refactor: update method calls in ReactorCoreEntity and ReactorPattern for consistency --- .../content/multiblock/core/ReactorCoreEntity.java | 2 +- .../content/multiblock/pattern/ReactorPattern.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java index b1468c99..06b88c52 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/core/ReactorCoreEntity.java @@ -29,7 +29,7 @@ public void tick() { BlockPos controllerPos = getBlockPosForReactor(); if (level.getBlockEntity(controllerPos) instanceof ReactorControllerBlockEntity reactorController) { - int heat = (int) reactorController.configuredPattern.getOrCreateTag().getDouble("heat"); + int heat = (int) reactorController.getConfiguredPattern().getOrCreateTag().getDouble("heat"); if (IHeat.HeatLevel.of(heat) == IHeat.HeatLevel.DANGER) { if (countdownTicks >= CNConfigs.server().explode.time.get()) { // 300 ticks = 15 secondes explodeReactorCore(level, getBlockPos()); diff --git a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java index 9519b980..c06e6977 100644 --- a/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java +++ b/src/main/java/net/nuclearteam/createnuclear/content/multiblock/pattern/ReactorPattern.java @@ -120,7 +120,7 @@ public BlockPos findControllerPos(BlockPos blockPos, Level level, List