• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyclopsMC / IntegratedDynamics / 16552051255

27 Jul 2025 01:58PM UTC coverage: 53.206% (+8.0%) from 45.161%
16552051255

push

github

rubensworks
Resolve minor TODOs

2888 of 8740 branches covered (33.04%)

Branch coverage included in aggregate %.

17341 of 29280 relevant lines covered (59.22%)

3.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/org/cyclops/integrateddynamics/inventory/container/ContainerLogicProgrammerBase.java
1
package org.cyclops.integrateddynamics.inventory.container;
2

3
import com.google.common.collect.Lists;
4
import net.minecraft.core.component.DataComponents;
5
import net.minecraft.network.chat.Component;
6
import net.minecraft.resources.ResourceLocation;
7
import net.minecraft.util.StringUtil;
8
import net.minecraft.world.Container;
9
import net.minecraft.world.SimpleContainer;
10
import net.minecraft.world.entity.player.Inventory;
11
import net.minecraft.world.entity.player.Player;
12
import net.minecraft.world.inventory.ClickType;
13
import net.minecraft.world.inventory.MenuType;
14
import net.minecraft.world.item.ItemStack;
15
import org.apache.commons.lang3.tuple.Pair;
16
import org.cyclops.cyclopscore.helper.IModHelpers;
17
import org.cyclops.cyclopscore.inventory.SimpleInventory;
18
import org.cyclops.cyclopscore.inventory.container.ScrollingInventoryContainer;
19
import org.cyclops.cyclopscore.inventory.slot.SlotSingleItem;
20
import org.cyclops.cyclopscore.persist.IDirtyMarkListener;
21
import org.cyclops.integrateddynamics.IntegratedDynamics;
22
import org.cyclops.integrateddynamics.RegistryEntries;
23
import org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;
24
import org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;
25
import org.cyclops.integrateddynamics.api.item.IVariableFacade;
26
import org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;
27
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;
28
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;
29
import org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;
30
import org.cyclops.integrateddynamics.core.helper.Helpers;
31
import org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;
32
import org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypes;
33
import org.cyclops.integrateddynamics.core.logicprogrammer.client.RenderPattern;
34
import org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;
35

36
import javax.annotation.Nullable;
37
import java.util.List;
38

39
/**
40
 * Base container for the logic programmer.
41
 * @author rubensworks
42
 */
43
public abstract class ContainerLogicProgrammerBase extends ScrollingInventoryContainer<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> implements IDirtyMarkListener {
44

45
    public static final int OUTPUT_X = 232;
46
    public static final int OUTPUT_Y = 110;
47

48
    public static final int BASE_X = 88;
49
    public static final int BASE_Y = 18;
50
    public static final int MAX_WIDTH = 160;
51
    public static final int MAX_HEIGHT = 87;
52

53
    protected static final IItemPredicate<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> FILTERER =
×
54
            (item, pattern) -> pattern.matcher(item.getMatchString()).matches()
×
55
                    || pattern.matcher(item.getSymbol()).matches();
×
56

57
    private final SimpleInventory writeSlot;
58
    private final SimpleInventory filterSlots;
59
    private ILogicProgrammerElement activeElement = null;
×
60
    private ILogicProgrammerElement temporarySlotsElement = null;
×
61
    private SimpleInventory temporaryInputSlots = null;
×
62
    private Component lastError;
63
    private LoadConfigListener loadConfigListener;
64

65
    private IValueType filterIn1 = null;
×
66
    private IValueType filterIn2 = null;
×
67
    private IValueType filterOut = null;
×
68

69
    private ScreenCallbackHandler screenCallbackHandler;
70

71
    private String lastLabel = "";
×
72

73
    public ContainerLogicProgrammerBase(@Nullable MenuType<?> type, int id, Inventory playerInventory) {
74
        super(type, id, playerInventory, new SimpleContainer(0), getElements(), FILTERER);
×
75
        this.writeSlot = new SimpleInventory(1, 1);
×
76
        this.filterSlots = new SimpleInventory(3, 1);
×
77
        this.filterSlots.addDirtyMarkListener(new FilterSlotListener(ValueDeseralizationContext.of(playerInventory.player.level())));
×
78
        this.writeSlot.addDirtyMarkListener(this);
×
79
        this.writeSlot.addDirtyMarkListener(loadConfigListener = new LoadConfigListener());
×
80
        this.temporaryInputSlots = new SimpleInventory(0, 1);
×
81
        initializeSlotsPre();
×
82
        initializeSlotsPost();
×
83
    }
×
84

85
    protected static List<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> getElements() {
86
        List<ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?>> elements = Lists.newLinkedList();
×
87
        for(ILogicProgrammerElementType type: LogicProgrammerElementTypes.REGISTRY.getTypes()) {
×
88
            elements.addAll(type.createElements());
×
89
        }
×
90
        return elements;
×
91
    }
92

93
    public void setScreenCallbackHandler(ScreenCallbackHandler screenCallbackHandler) {
94
        this.screenCallbackHandler = screenCallbackHandler;
×
95
    }
×
96

97
    public ScreenCallbackHandler getGui() {
98
        return this.screenCallbackHandler;
×
99
    }
100

101
    protected void initializeSlotsPre() {
102
        addSlot(new SlotVariable(writeSlot, 0, OUTPUT_X, OUTPUT_Y) {
×
103
            @Override
104
            public void setChanged() {
105
                // We don't call super here to avoid dirty mark listeners to be called twice, which can cause issues with loading and immediate overwriting.
106
                // This is because SimpleInventory will already call dirty mark listeners when calling setItem.
107
                // Strictly this behaviour in SimpleInventory is not required, but due to backwards-compat, we keep it.
108
                // TODO: refactor this in SimpleInventory in next major?
109
            }
×
110
        });
111
        SlotSingleItem filterSlotIn1 = new SlotVariable(filterSlots, 0, 6, 218);
×
112
        SlotSingleItem filterSlotIn2 = new SlotVariable(filterSlots, 1, 24, 218);
×
113
        SlotSingleItem filterSlotOut = new SlotVariable(filterSlots, 2, 58, 218);
×
114
        filterSlotIn1.setPhantom(true);
×
115
        filterSlotIn2.setPhantom(true);
×
116
        filterSlotOut.setPhantom(true);
×
117
        addSlot(filterSlotIn1);
×
118
        addSlot(filterSlotIn2);
×
119
        addSlot(filterSlotOut);
×
120
    }
×
121

122
    protected void initializeSlotsPost() {
123
        addPlayerInventory((Inventory) getPlayerIInventory(), 88, 131);
×
124
    }
×
125

126
    @Override
127
    public int getPageSize() {
128
        return 10;
×
129
    }
130

131
    @Override
132
    protected boolean isAssertInventorySize() {
133
        return false;
×
134
    }
135

136
    @Override
137
    protected int getSizeInventory() {
138
        return 1;
×
139
    }
140

141
    public void setActiveElementById(ResourceLocation typeId, ResourceLocation elementId) {
142
        ILogicProgrammerElementType type = LogicProgrammerElementTypes.REGISTRY.getType(typeId);
×
143
        if (type != null) {
×
144
            ILogicProgrammerElement element = type.getByName(elementId);
×
145
            if(!LogicProgrammerElementTypes.areEqual(getActiveElement(), element)) {
×
146
                setActiveElement(element, 0, 0);
×
147
                onDirty();
×
148
            }
149
        } else {
×
150
            setActiveElement(null, 0, 0);
×
151
        }
152
    }
×
153

154
    /**
155
     * Set the new active element.
156
     * @param activeElement The new element.
157
     * @param baseX The slots X coordinate
158
     * @param baseY The slots Y coordinate
159
     */
160
    public void setActiveElement(final ILogicProgrammerElement activeElement, int baseX, int baseY) {
161
        if(this.activeElement != null) {
×
162
            this.activeElement.deactivate();
×
163
        }
164
        this.activeElement = activeElement;
×
165

166
        this.lastError = null;
×
167
        this.activeElement = activeElement;
×
168

169
        this.setElementInventory(this.activeElement, baseX, baseY);
×
170

171
        if(activeElement != null) {
×
172
            activeElement.activate();
×
173
        }
174
    }
×
175

176
    /**
177
     * Set the new active element.
178
     * @param element The new element.
179
     * @param baseX The slots X coordinate
180
     * @param baseY The slots Y coordinate
181
     */
182
    public void setElementInventory(final ILogicProgrammerElement element, int baseX, int baseY) {
183
        this.lastError = null;
×
184

185
        // This assumes that there is only one other slot, the remaining slots will be erased!
186
        // (We can do this because they are all ghost slots)
187
        lastSlots.clear();
×
188
        slots.clear();
×
189
        remoteSlots.clear();
×
190
        initializeSlotsPre();
×
191
        this.temporaryInputSlots.removeDirtyMarkListener(this);
×
192
        this.temporaryInputSlots = new SimpleInventory(element == null ? 0 : element.getRenderPattern().getSlotPositions().length, element == null ? 0 : element.getItemStackSizeLimit());
×
193
        temporaryInputSlots.addDirtyMarkListener(this);
×
194
        this.temporarySlotsElement = element;
×
195
        if(element != null) {
×
196
            Pair<Integer, Integer>[] slotPositions = element.getRenderPattern().getSlotPositions();
×
197
            for (int i = 0; i < temporaryInputSlots.getContainerSize(); i++) {
×
198
                addSlot(element.createSlot(temporaryInputSlots, i, 1 + baseX + slotPositions[i].getLeft(),
×
199
                        1 + baseY + slotPositions[i].getRight()));
×
200
            }
201
        }
202
        initializeSlotsPost();
×
203
        this.lastLabel = "";
×
204
    }
×
205

206
    public boolean canWriteActiveElementPre() {
207
        if(activeElement != null) {
×
208
            return activeElement.canWriteElementPre();
×
209
        }
210
        return false;
×
211
    }
212

213
    public boolean canWriteActiveElement() {
214
        if(!canWriteActiveElementPre()) {
×
215
            return false;
×
216
        }
217
        lastError = activeElement.validate();
×
218
        return lastError == null;
×
219
    }
220

221
    public ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?> getActiveElement() {
222
        return activeElement;
×
223
    }
224

225
    @Override
226
    public void removed(Player player) {
227
        super.removed(player);
×
228
        if (!player.level().isClientSide()) {
×
229
            returnWriteItemToPlayer();
×
230
        }
231
    }
×
232

233
    public void onLabelPacket(String label) {
234
        this.lastLabel = label;
×
235
        labelCurrent();
×
236
    }
×
237

238
    protected void labelCurrent() {
239
        ItemStack itemStack = writeSlot.getItem(0);
×
240
        if(!itemStack.isEmpty()) {
×
241
            IVariableFacade variableFacade = RegistryEntries.ITEM_VARIABLE.get().getVariableFacade(ValueDeseralizationContext.of(player.level()), itemStack);
×
242
            if(this.lastLabel != null && variableFacade.isValid()) {
×
243
                LabelsWorldStorage.Access.getInstance(IntegratedDynamics._instance).get().put(variableFacade.getId(), this.lastLabel);
×
244
            }
245
        }
246
    }
×
247

248
    protected ItemStack writeElementInfo() {
249
        ItemStack itemStack = writeSlot.getItem(0);
×
250
        ItemStack result = getActiveElement().writeElement(player, itemStack.copy());
×
251
        return result;
×
252
    }
253

254
    @Override
255
    public void onDirty() {
256
        ILogicProgrammerElement activeElement = getActiveElement();
×
257
        if(activeElement != null) {
×
258
            for (int i = 0; i < temporaryInputSlots.getContainerSize(); i++) {
×
259
                ItemStack itemStack = temporaryInputSlots.getItem(i);
×
260
                temporarySlotsElement.onInputSlotUpdated(player, i, itemStack);
×
261
            }
262
        }
263

264
        ItemStack itemStack = writeSlot.getItem(0);
×
265
        if(canWriteActiveElement() && !itemStack.isEmpty()) {
×
266
            // If the variable has a vanilla custom name, make sure we inherit it as variable label
267
            if (itemStack.has(DataComponents.CUSTOM_NAME)) {
×
268
                this.lastLabel = itemStack.getHoverName().getString();
×
269
            }
270

271
            ItemStack outputStack = writeElementInfo();
×
272
            writeSlot.removeDirtyMarkListener(this);
×
273
            writeSlot.removeDirtyMarkListener(loadConfigListener);
×
274
            writeSlot.setItem(0, outputStack);
×
275
            if(!StringUtil.isNullOrEmpty(this.lastLabel)) {
×
276
                labelCurrent();
×
277
            }
278
            writeSlot.addDirtyMarkListener(this);
×
279
            writeSlot.addDirtyMarkListener(loadConfigListener);
×
280
        }
281
    }
×
282

283
    protected void loadConfigFrom(ItemStack itemStack) {
284
        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);
×
285
        IVariableFacade variableFacade = registry.handle(ValueDeseralizationContext.of(player.level()), itemStack);
×
286
        for(ILogicProgrammerElement<RenderPattern, ContainerScreenLogicProgrammerBase<?>, ContainerLogicProgrammerBase, ?> element : getUnfilteredItems()) {
×
287
            if(element.isFor(variableFacade)) {
×
288
                writeSlot.removeDirtyMarkListener(this);
×
289
                writeSlot.removeDirtyMarkListener(loadConfigListener);
×
290
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
291
                    getGui().handleElementActivation(element, false);
×
292
                } else {
293
                    setActiveElement(element, 0, 0);
×
294
                }
295
                temporaryInputSlots.removeDirtyMarkListener(this);
×
296
                element.loadElement(variableFacade);
×
297
                if (IModHelpers.get().getMinecraftHelpers().isClientSideThread()) {
×
298
                    element.getClient().setValueInGui(getGui().getOperatorConfigPattern());
×
299
                } else {
300
                    element.setValueInContainer(this);
×
301
                }
302
                writeSlot.addDirtyMarkListener(this);
×
303
                writeSlot.addDirtyMarkListener(loadConfigListener);
×
304
                temporaryInputSlots.addDirtyMarkListener(this);
×
305
            }
306
        }
×
307
    }
×
308

309
    public Component getLastError() {
310
        return this.lastError;
×
311
    }
312

313
    public SimpleInventory getTemporaryInputSlots() {
314
        return this.temporaryInputSlots;
×
315
    }
316

317
    public boolean hasWriteItemInSlot() {
318
        return !this.writeSlot.getItem(0).isEmpty();
×
319
    }
320

321
    public void returnWriteItemToPlayer() {
322
        if (hasWriteItemInSlot()) {
×
323
            ItemStack itemStack = writeSlot.getItem(0);
×
324
            Helpers.returnItemToPlayer(player, itemStack);
×
325
            writeSlot.setItem(0, ItemStack.EMPTY);
×
326
        }
327
    }
×
328

329
    @Override
330
    protected boolean additionalApplies(ILogicProgrammerElement item) {
331
        return (
×
332
                ((filterIn1 == null || item.matchesInput(filterIn1)) && (filterIn2 == null || item.matchesInput(filterIn2))) || (filterIn1 == null && filterIn2 == null))
×
333
                && (filterOut == null || item.matchesOutput(filterOut));
×
334
    }
335

336
    @Override
337
    public void clicked(int slotId, int mouseButton, ClickType clickType, Player player) {
338
        // Handle cases where the client may have more (phantom) slots than the server.
339
        if (slotId >= this.slots.size() || (this.activeElement != null
×
340
                && this.slots.size() > slotId && slotId >= 0
×
341
                && this.activeElement.slotClick(slotId, this.getSlot(slotId), mouseButton, clickType, player))) {
×
342
            return;
×
343
        }
344
        super.clicked(slotId, mouseButton, clickType, player);
×
345
    }
×
346

347
    /**
348
     * Load existing operator data when a variable card is inserted into the write slot
349
     */
350
    protected class LoadConfigListener implements IDirtyMarkListener {
×
351

352
        @Override
353
        public void onDirty() {
354
            if (activeElement == null) {
×
355
                ItemStack itemStack = writeSlot.getItem(0);
×
356
                if (!itemStack.isEmpty()) {
×
357
                    ContainerLogicProgrammerBase.this.loadConfigFrom(itemStack);
×
358
                }
359
            }
360
        }
×
361

362
    }
363

364
    /**
365
     * Filter LP elements based on the filter value types.
366
     */
367
    protected class FilterSlotListener implements IDirtyMarkListener {
368

369
        private final ValueDeseralizationContext valueDeseralizationContext;
370

371
        public FilterSlotListener(ValueDeseralizationContext valueDeseralizationContext) {
×
372
            this.valueDeseralizationContext = valueDeseralizationContext;
×
373
        }
×
374

375
        protected IValueType getValueType(Container inventory, int slot) {
376
            IVariableFacadeHandlerRegistry handler = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);
×
377
            if(inventory.getItem(slot) != null) {
×
378
                IVariableFacade variableFacade = handler.handle(valueDeseralizationContext, inventory.getItem(slot));
×
379
                if(variableFacade.isValid()) {
×
380
                    return variableFacade.getOutputType();
×
381
                }
382
            }
383
            return null;
×
384
        }
385

386
        @Override
387
        public void onDirty() {
388
            IValueType filterIn1Prev = filterIn1;
×
389
            IValueType filterIn2Prev = filterIn2;
×
390
            IValueType filterOutPrev = filterOut;
×
391

392
            filterIn1 = getValueType(filterSlots, 0);
×
393
            filterIn2 = getValueType(filterSlots, 1);
×
394
            filterOut = getValueType(filterSlots, 2);
×
395
            if (filterIn1Prev != filterIn1 || filterIn2Prev != filterIn2 || filterOutPrev != filterOut) {
×
396
                refreshFilter();
×
397
            }
398
        }
×
399

400
    }
401

402
    public static interface ScreenCallbackHandler {
403
        public boolean handleElementActivation(ILogicProgrammerElement element, boolean sendToServer);
404
        public RenderPattern getOperatorConfigPattern();
405
    }
406

407
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc