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

CyclopsMC / IntegratedDynamics / 15366166327

31 May 2025 05:58PM UTC coverage: 44.766% (-0.5%) from 45.303%
15366166327

push

github

rubensworks
Bump mod version

2565 of 8515 branches covered (30.12%)

Branch coverage included in aggregate %.

11734 of 23427 relevant lines covered (50.09%)

2.38 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.server.level.ServerPlayer;
8
import net.minecraft.util.StringUtil;
9
import net.minecraft.world.Container;
10
import net.minecraft.world.SimpleContainer;
11
import net.minecraft.world.entity.player.Inventory;
12
import net.minecraft.world.entity.player.Player;
13
import net.minecraft.world.inventory.ClickType;
14
import net.minecraft.world.inventory.MenuType;
15
import net.minecraft.world.item.ItemStack;
16
import net.neoforged.api.distmarker.Dist;
17
import net.neoforged.api.distmarker.OnlyIn;
18
import org.apache.commons.lang3.tuple.Pair;
19
import org.cyclops.cyclopscore.helper.MinecraftHelpers;
20
import org.cyclops.cyclopscore.inventory.SimpleInventory;
21
import org.cyclops.cyclopscore.inventory.container.ScrollingInventoryContainer;
22
import org.cyclops.cyclopscore.inventory.slot.SlotSingleItem;
23
import org.cyclops.cyclopscore.persist.IDirtyMarkListener;
24
import org.cyclops.integrateddynamics.IntegratedDynamics;
25
import org.cyclops.integrateddynamics.RegistryEntries;
26
import org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;
27
import org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;
28
import org.cyclops.integrateddynamics.api.item.IVariableFacade;
29
import org.cyclops.integrateddynamics.api.item.IVariableFacadeHandlerRegistry;
30
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElement;
31
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;
32
import org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;
33
import org.cyclops.integrateddynamics.core.inventory.container.slot.SlotVariable;
34
import org.cyclops.integrateddynamics.core.logicprogrammer.LogicProgrammerElementTypes;
35
import org.cyclops.integrateddynamics.core.persist.world.LabelsWorldStorage;
36

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

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

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

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

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

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

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

70
    @OnlyIn(Dist.CLIENT)
71
    private ContainerScreenLogicProgrammerBase gui;
72

73
    private String lastLabel = "";
×
74

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

87
    protected static List<ILogicProgrammerElement> getElements() {
88
        List<ILogicProgrammerElement> elements = Lists.newLinkedList();
×
89
        for(ILogicProgrammerElementType type: LogicProgrammerElementTypes.REGISTRY.getTypes()) {
×
90
            elements.addAll(type.createElements());
×
91
        }
×
92
        return elements;
×
93
    }
94

95
    @OnlyIn(Dist.CLIENT)
96
    public void setGui(ContainerScreenLogicProgrammerBase gui) {
97
        this.gui = gui;
×
98
    }
×
99

100
    @OnlyIn(Dist.CLIENT)
101
    public ContainerScreenLogicProgrammerBase getGui() {
102
        return this.gui;
×
103
    }
104

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

126
    protected void initializeSlotsPost() {
127
        addPlayerInventory((Inventory) getPlayerIInventory(), 88, 131);
×
128
    }
×
129

130
    @Override
131
    public int getPageSize() {
132
        return 10;
×
133
    }
134

135
    @Override
136
    protected boolean isAssertInventorySize() {
137
        return false;
×
138
    }
139

140
    @Override
141
    protected int getSizeInventory() {
142
        return 1;
×
143
    }
144

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

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

170
        this.lastError = null;
×
171
        this.activeElement = activeElement;
×
172

173
        this.setElementInventory(this.activeElement, baseX, baseY);
×
174

175
        if(activeElement != null) {
×
176
            activeElement.activate();
×
177
        }
178
    }
×
179

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

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

210
    public boolean canWriteActiveElementPre() {
211
        if(activeElement != null) {
×
212
            return activeElement.canWriteElementPre();
×
213
        }
214
        return false;
×
215
    }
216

217
    public boolean canWriteActiveElement() {
218
        if(!canWriteActiveElementPre()) {
×
219
            return false;
×
220
        }
221
        lastError = activeElement.validate();
×
222
        return lastError == null;
×
223
    }
224

225
    public ILogicProgrammerElement getActiveElement() {
226
        return activeElement;
×
227
    }
228

229
    @Override
230
    public void removed(Player player) {
231
        super.removed(player);
×
232
        if (!player.level().isClientSide()) {
×
233
            returnWriteItemToPlayer();
×
234
        }
235
    }
×
236

237
    public void onLabelPacket(String label) {
238
        this.lastLabel = label;
×
239
        labelCurrent();
×
240
    }
×
241

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

252
    protected ItemStack writeElementInfo() {
253
        ItemStack itemStack = writeSlot.getItem(0);
×
254
        ItemStack result = getActiveElement().writeElement(player, itemStack.copy());
×
255
        return result;
×
256
    }
257

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

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

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

287
    protected void loadConfigFrom(ItemStack itemStack) {
288
        IVariableFacadeHandlerRegistry registry = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);
×
289
        IVariableFacade variableFacade = registry.handle(ValueDeseralizationContext.of(player.level()), itemStack);
×
290
        for(ILogicProgrammerElement element : getUnfilteredItems()) {
×
291
            if(element.isFor(variableFacade)) {
×
292
                writeSlot.removeDirtyMarkListener(this);
×
293
                writeSlot.removeDirtyMarkListener(loadConfigListener);
×
294
                if (MinecraftHelpers.isClientSideThread()) {
×
295
                    getGui().handleElementActivation(element, false);
×
296
                } else {
297
                    setActiveElement(element, 0, 0);
×
298
                }
299
                temporaryInputSlots.removeDirtyMarkListener(this);
×
300
                element.loadElement(variableFacade);
×
301
                if (MinecraftHelpers.isClientSideThread()) {
×
302
                    element.setValueInGui(getGui().getOperatorConfigPattern());
×
303
                } else {
304
                    element.setValueInContainer(this);
×
305
                }
306
                writeSlot.addDirtyMarkListener(this);
×
307
                writeSlot.addDirtyMarkListener(loadConfigListener);
×
308
                temporaryInputSlots.addDirtyMarkListener(this);
×
309
            }
310
        }
×
311
    }
×
312

313
    public Component getLastError() {
314
        return this.lastError;
×
315
    }
316

317
    public SimpleInventory getTemporaryInputSlots() {
318
        return this.temporaryInputSlots;
×
319
    }
320

321
    public boolean hasWriteItemInSlot() {
322
        return !this.writeSlot.getItem(0).isEmpty();
×
323
    }
324

325
    public void returnWriteItemToPlayer() {
326
        if (hasWriteItemInSlot()) {
×
327
            ItemStack itemStack = writeSlot.getItem(0);
×
328
            if (player.isAlive() && (!(player instanceof ServerPlayer) || !((ServerPlayer)player).hasDisconnected())) {
×
329
                player.getInventory().placeItemBackInInventory(itemStack);
×
330
            } else {
331
                player.drop(itemStack, false);
×
332
            }
333
            writeSlot.setItem(0, ItemStack.EMPTY);
×
334
        }
335
    }
×
336

337
    @Override
338
    protected boolean additionalApplies(ILogicProgrammerElement item) {
339
        return (
×
340
                ((filterIn1 == null || item.matchesInput(filterIn1)) && (filterIn2 == null || item.matchesInput(filterIn2))) || (filterIn1 == null && filterIn2 == null))
×
341
                && (filterOut == null || item.matchesOutput(filterOut));
×
342
    }
343

344
    @Override
345
    public void clicked(int slotId, int mouseButton, ClickType clickType, Player player) {
346
        // Handle cases where the client may have more (phantom) slots than the server.
347
        if (slotId >= this.slots.size() || (this.activeElement != null
×
348
                && this.slots.size() > slotId && slotId >= 0
×
349
                && this.activeElement.slotClick(slotId, this.getSlot(slotId), mouseButton, clickType, player))) {
×
350
            return;
×
351
        }
352
        super.clicked(slotId, mouseButton, clickType, player);
×
353
    }
×
354

355
    /**
356
     * Load existing operator data when a variable card is inserted into the write slot
357
     */
358
    protected class LoadConfigListener implements IDirtyMarkListener {
×
359

360
        @Override
361
        public void onDirty() {
362
            if (activeElement == null) {
×
363
                ItemStack itemStack = writeSlot.getItem(0);
×
364
                if (!itemStack.isEmpty()) {
×
365
                    ContainerLogicProgrammerBase.this.loadConfigFrom(itemStack);
×
366
                }
367
            }
368
        }
×
369

370
    }
371

372
    /**
373
     * Filter LP elements based on the filter value types.
374
     */
375
    protected class FilterSlotListener implements IDirtyMarkListener {
376

377
        private final ValueDeseralizationContext valueDeseralizationContext;
378

379
        public FilterSlotListener(ValueDeseralizationContext valueDeseralizationContext) {
×
380
            this.valueDeseralizationContext = valueDeseralizationContext;
×
381
        }
×
382

383
        protected IValueType getValueType(Container inventory, int slot) {
384
            IVariableFacadeHandlerRegistry handler = IntegratedDynamics._instance.getRegistryManager().getRegistry(IVariableFacadeHandlerRegistry.class);
×
385
            if(inventory.getItem(slot) != null) {
×
386
                IVariableFacade variableFacade = handler.handle(valueDeseralizationContext, inventory.getItem(slot));
×
387
                if(variableFacade.isValid()) {
×
388
                    return variableFacade.getOutputType();
×
389
                }
390
            }
391
            return null;
×
392
        }
393

394
        @Override
395
        public void onDirty() {
396
            IValueType filterIn1Prev = filterIn1;
×
397
            IValueType filterIn2Prev = filterIn2;
×
398
            IValueType filterOutPrev = filterOut;
×
399

400
            filterIn1 = getValueType(filterSlots, 0);
×
401
            filterIn2 = getValueType(filterSlots, 1);
×
402
            filterOut = getValueType(filterSlots, 2);
×
403
            if (filterIn1Prev != filterIn1 || filterIn2Prev != filterIn2 || filterOutPrev != filterOut) {
×
404
                refreshFilter();
×
405
            }
406
        }
×
407

408
    }
409

410
}
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