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

CyclopsMC / IntegratedDynamics / 19675879296

25 Nov 2025 04:01PM UTC coverage: 45.158% (+0.3%) from 44.824%
19675879296

push

github

rubensworks
Bump mod version

2618 of 8556 branches covered (30.6%)

Branch coverage included in aggregate %.

11861 of 23507 relevant lines covered (50.46%)

2.39 hits per line

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

43.94
/src/main/java/org/cyclops/integrateddynamics/core/part/panel/PartTypePanelVariableDriven.java
1
package org.cyclops.integrateddynamics.core.part.panel;
2

3
import com.google.common.collect.Lists;
4
import lombok.Getter;
5
import lombok.Setter;
6
import net.minecraft.ChatFormatting;
7
import net.minecraft.core.BlockPos;
8
import net.minecraft.core.Direction;
9
import net.minecraft.nbt.CompoundTag;
10
import net.minecraft.nbt.Tag;
11
import net.minecraft.network.RegistryFriendlyByteBuf;
12
import net.minecraft.network.chat.Component;
13
import net.minecraft.network.chat.MutableComponent;
14
import net.minecraft.resources.ResourceLocation;
15
import net.minecraft.server.level.ServerPlayer;
16
import net.minecraft.world.InteractionHand;
17
import net.minecraft.world.InteractionResult;
18
import net.minecraft.world.MenuProvider;
19
import net.minecraft.world.entity.player.Inventory;
20
import net.minecraft.world.entity.player.Player;
21
import net.minecraft.world.inventory.AbstractContainerMenu;
22
import net.minecraft.world.item.ItemStack;
23
import net.minecraft.world.level.Level;
24
import net.minecraft.world.level.block.Block;
25
import net.minecraft.world.level.block.state.BlockState;
26
import net.minecraft.world.phys.BlockHitResult;
27
import org.apache.commons.lang3.tuple.Triple;
28
import org.cyclops.cyclopscore.config.extendedconfig.BlockConfig;
29
import org.cyclops.cyclopscore.helper.BlockHelpers;
30
import org.cyclops.integrateddynamics.IntegratedDynamics;
31
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
32
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
33
import org.cyclops.integrateddynamics.api.evaluate.variable.IValueType;
34
import org.cyclops.integrateddynamics.api.evaluate.variable.IValueTypeListProxy;
35
import org.cyclops.integrateddynamics.api.evaluate.variable.IVariable;
36
import org.cyclops.integrateddynamics.api.evaluate.variable.ValueDeseralizationContext;
37
import org.cyclops.integrateddynamics.api.network.INetwork;
38
import org.cyclops.integrateddynamics.api.network.IPartNetwork;
39
import org.cyclops.integrateddynamics.api.network.event.INetworkEvent;
40
import org.cyclops.integrateddynamics.api.part.IPartContainer;
41
import org.cyclops.integrateddynamics.api.part.IPartTypeActiveVariable;
42
import org.cyclops.integrateddynamics.api.part.PartPos;
43
import org.cyclops.integrateddynamics.api.part.PartTarget;
44
import org.cyclops.integrateddynamics.core.block.IgnoredBlock;
45
import org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;
46
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;
47
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;
48
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
49
import org.cyclops.integrateddynamics.core.helper.L10NValues;
50
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
51
import org.cyclops.integrateddynamics.core.helper.PartHelpers;
52
import org.cyclops.integrateddynamics.core.helper.WrenchHelpers;
53
import org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;
54
import org.cyclops.integrateddynamics.core.part.PartStateActiveVariableBase;
55
import org.cyclops.integrateddynamics.core.part.PartTypeBase;
56
import org.cyclops.integrateddynamics.inventory.container.ContainerPartPanelVariableDriven;
57

58
import javax.annotation.Nullable;
59
import java.util.List;
60
import java.util.Map;
61
import java.util.Optional;
62

63
/**
64
 * A panel part that is driven by a contained variable.
65
 * @author rubensworks
66
 */
67
public abstract class PartTypePanelVariableDriven<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>> extends PartTypePanel<P, S> implements IPartTypeActiveVariable<P, S> {
68

69
    public PartTypePanelVariableDriven(String name) {
70
        super(name);
3✔
71
    }
1✔
72

73
    @Override
74
    protected Block createBlock(BlockConfig blockConfig) {
75
        return new IgnoredBlockStatus();
×
76
    }
77

78
    @Override
79
    protected Map<Class<? extends INetworkEvent>, IEventAction> constructNetworkEventActions() {
80
        Map<Class<? extends INetworkEvent>, IEventAction> actions = super.constructNetworkEventActions();
3✔
81
        IEventAction<P, S, INetworkEvent> updateEventListener = (network, target, state, event) -> NetworkHelpers
5✔
82
                .getPartNetwork(network).ifPresent(partNetwork -> onVariableContentsUpdated(partNetwork, target, state));
12✔
83
        actions.put(VariableContentsUpdatedEvent.class, updateEventListener);
5✔
84
        return actions;
2✔
85
    }
86

87
    @Override
88
    public void addDrops(PartTarget target, S state, List<ItemStack> itemStacks, boolean dropMainElement, boolean saveState) {
89
        for(int i = 0; i < state.getInventory().getContainerSize(); i++) {
×
90
            ItemStack itemStack = state.getInventory().getItem(i);
×
91
            if(!itemStack.isEmpty()) {
×
92
                itemStacks.add(itemStack);
×
93
            }
94
        }
95
        state.getInventory().clearContent();
×
96
        state.onVariableContentsUpdated((P) this, target);
×
97
        super.addDrops(target, state, itemStacks, dropMainElement, saveState);
×
98
    }
×
99

100
    @Override
101
    public boolean isUpdate(S state) {
102
        return true;
2✔
103
    }
104

105
    @Override
106
    public void update(INetwork network, IPartNetwork partNetwork, PartTarget target, S state) {
107
        super.update(network, partNetwork, target, state);
6✔
108
        IValue lastValue = state.getDisplayValue();
3✔
109
        IValue newValue = null;
2✔
110
        Level level = target.getCenter().getPos().getLevel(true);
6✔
111
        if(state.hasVariable()) {
3✔
112
            try {
113
                IVariable variable = state.getVariable(network, partNetwork, ValueDeseralizationContext.of(level));
7✔
114
                if(variable != null) {
2✔
115
                    newValue = variable.getValue();
3✔
116

117
                    if (state.isRetryEvaluation()) {
3!
118
                        state.setRetryEvaluation(false);
×
119
                        state.addGlobalError(null);
×
120
                    }
121
                }
122
            } catch (EvaluationException e) {
1✔
123
                if (!state.isRetryEvaluation()) {
3✔
124
                    state.addGlobalError(e.getErrorMessage());
4✔
125
                    if (e.isRetryEvaluation()) {
3!
126
                        state.setRetryEvaluation(true);
4✔
127
                    } else {
128
                        e.addResolutionListeners(() -> state.onVariableContentsUpdated((P) this, target));
×
129
                    }
130
                }
131
            }
1✔
132
        }
133
        if(!ValueHelpers.areValuesEqual(lastValue, newValue)) {
4✔
134
            onValueChanged(network, partNetwork, target, state, lastValue, newValue);
8✔
135

136
            // We can't call state.sendUpdate() here, so we must trigger a block update manually.
137
            // This was the cause of issue #46 which made it so that values that change after one tick are
138
            // NOT sent to the client.
139
            // This was because in each server tick, all tiles are first updated and then the networks.
140
            // Since sendUpdate marks a flag for the part to update, this caused a loss of one tick.
141
            // For example:
142
            // tick-0: Tile tick
143
            // tick-0: Part tick: update a value, and mark part to invalidate
144
            // tick-0: -- send all block updates to client ---
145
            // tick-1: Tile tick: notices and update, marks a block invalidate
146
            // tick-1: Part tick: update the value again, the old value has still not been sent here!
147
            // tick-1: -- send all block updates to client --- This will contain the value that was set in tick-1.
148
            state.onDirty();
2✔
149
            BlockHelpers.markForUpdate(level, target.getCenter().getPos().getBlockPos());
6✔
150
        }
151
    }
1✔
152

153
    @Override
154
    public boolean hasActiveVariable(IPartNetwork network, PartTarget target, S partState) {
155
        return partState.hasVariable();
×
156
    }
157

158
    @Override
159
    public <V extends IValue> IVariable<V> getActiveVariable(INetwork network, IPartNetwork partNetwork, PartTarget target, S partState) {
160
        return partState.getVariable(network, partNetwork, ValueDeseralizationContext.of(target.getCenter().getPos().getLevel(true)));
×
161
    }
162

163
    protected void onValueChanged(INetwork network, IPartNetwork partNetwork, PartTarget target, S state,
164
                                  IValue lastValue, IValue newValue) {
165
        if (newValue == null) {
2✔
166
            state.setDisplayValue(null);
4✔
167
        } else {
168
            IValue materializedValue = null;
2✔
169
            try {
170
                if (newValue.getType() == ValueTypes.LIST) {
4✔
171
                    IValueTypeListProxy<IValueType<IValue>, IValue> original = ((ValueTypeList.ValueList) newValue).getRawValue();
4✔
172
                    if (original.getLength() > ValueTypeList.MAX_RENDER_LINES) {
4!
173
                        List<IValue> list = Lists.newArrayList();
×
174
                        for (int i = 0; i < ValueTypeList.MAX_RENDER_LINES; i++) {
×
175
                            list.add(original.get(i));
×
176
                        }
177
                        newValue = ValueTypeList.ValueList.ofList(original.getValueType(), list);
×
178
                    }
179
                }
180
                materializedValue = newValue.getType().materialize(newValue);
5✔
181
            } catch (EvaluationException e) {
×
182
                state.addGlobalError(e.getErrorMessage());
×
183
                e.addResolutionListeners(() -> state.addGlobalError(null)); // TODO: also change here?
×
184
            }
1✔
185
            state.setDisplayValue(materializedValue);
3✔
186
        }
187
    }
1✔
188

189
    @Override
190
    public Optional<MenuProvider> getContainerProvider(PartPos pos) {
191
        return Optional.of(new MenuProvider() {
×
192
            @Override
193
            public Component getDisplayName() {
194
                return Component.translatable(getTranslationKey());
×
195
            }
196

197
            @Nullable
198
            @Override
199
            public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player playerEntity) {
200
                Triple<IPartContainer, PartTypeBase, PartTarget> data = PartHelpers.getContainerPartConstructionData(pos);
×
201
                PartTypePanelVariableDriven.State partState = (PartTypePanelVariableDriven.State) data.getLeft().getPartState(data.getRight().getCenter().getSide());
×
202
                return new ContainerPartPanelVariableDriven(id, playerInventory, partState.getInventory(),
×
203
                        Optional.of(data.getRight()), Optional.of(data.getLeft()), (PartTypePanelVariableDriven<?, ?>) data.getMiddle());
×
204
            }
205

206
            @Override
207
            public boolean shouldTriggerClientSideContainerClosingOnOpen() {
208
                return false;
×
209
            }
210
        });
211
    }
212

213
    @Override
214
    public void writeExtraGuiData(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) {
215
        // Write inventory size
216
        IPartContainer partContainer = PartHelpers.getPartContainerChecked(pos);
×
217
        PartTypePanelVariableDriven.State partState = (PartTypePanelVariableDriven.State) partContainer.getPartState(pos.getSide());
×
218
        packetBuffer.writeInt(partState.getInventory().getContainerSize());
×
219

220
        super.writeExtraGuiData(packetBuffer, pos, player);
×
221
    }
×
222

223
    protected IgnoredBlockStatus.Status getStatus(PartTypePanelVariableDriven.State state) {
224
        IgnoredBlockStatus.Status status = IgnoredBlockStatus.Status.INACTIVE;
2✔
225
        if (state != null && !state.getInventory().isEmpty()) {
6!
226
            if (state.hasVariable() && state.isEnabled()) {
6!
227
                status = IgnoredBlockStatus.Status.ACTIVE;
3✔
228
            } else {
229
                status = IgnoredBlockStatus.Status.ERROR;
2✔
230
            }
231
        }
232
        return status;
2✔
233
    }
234

235
    @Override
236
    public BlockState getBlockState(IPartContainer partContainer,
237
                                    Direction side) {
238
        IgnoredBlockStatus.Status status = getStatus(partContainer != null
5!
239
                ? (PartTypePanelVariableDriven.State) partContainer.getPartState(side) : null);
5✔
240
        return getBlock().defaultBlockState()
6✔
241
                .setValue(IgnoredBlock.FACING, side)
4✔
242
                .setValue(IgnoredBlockStatus.STATUS, status);
2✔
243
    }
244

245
    protected void onVariableContentsUpdated(IPartNetwork network, PartTarget target, S state) {
246
        state.onVariableContentsUpdated((P) this, target);
4✔
247
    }
1✔
248

249
    @Override
250
    public InteractionResult onPartActivated(final S partState, BlockPos pos, Level world, Player player, InteractionHand hand,
251
                                             ItemStack heldItem, BlockHitResult hit) {
252
        if(WrenchHelpers.isWrench(player, heldItem, world, pos, hit.getDirection())) {
×
253
            WrenchHelpers.wrench(player, heldItem, world, pos, hit.getDirection(),
×
254
                    (player1, pos1, parameter) -> partState.setFacingRotation(partState.getFacingRotation().getClockWise()));
×
255
            return InteractionResult.SUCCESS;
×
256
        }
257
        return super.onPartActivated(partState, pos, world, player, hand, heldItem, hit);
×
258
    }
259

260
    @Override
261
    public void loadTooltip(S state, List<Component> lines) {
262
        if (!state.getInventory().isEmpty()) {
×
263
            if (state.hasVariable() && state.isEnabled()) {
×
264
                IValue value = state.getDisplayValue();
×
265
                if(value != null) {
×
266
                    IValueType valueType = value.getType();
×
267
                    lines.add(Component.translatable(
×
268
                            L10NValues.PART_TOOLTIP_DISPLAY_ACTIVEVALUE,
269
                            valueType.toCompactString(value).withStyle(valueType.getDisplayColorFormat()),
×
270
                            Component.translatable(valueType.getTranslationKey())));
×
271
                }
272
            } else {
×
273
                lines.add(Component.translatable(L10NValues.PART_TOOLTIP_ERRORS).withStyle(ChatFormatting.RED));
×
274
                for (MutableComponent error : state.getGlobalErrors()) {
×
275
                    lines.add(error.withStyle(ChatFormatting.RED));
×
276
                }
×
277
            }
278
        } else {
279
            lines.add(Component.translatable(L10NValues.PART_TOOLTIP_INACTIVE));
×
280
        }
281
        super.loadTooltip(state, lines);
×
282
    }
×
283

284
    @Override
285
    public boolean shouldTriggerBlockRenderUpdate(@Nullable S oldPartState, @Nullable S newPartState) {
286
        return super.shouldTriggerBlockRenderUpdate(oldPartState, newPartState)
×
287
                || getStatus(oldPartState) != getStatus(newPartState);
×
288
    }
289

290
    public static abstract class State<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>> extends PartStateActiveVariableBase<P> {
291

292
        @Getter
3✔
293
        @Setter
4✔
294
        private IValue displayValue;
295
        @Getter
3✔
296
        @Setter
×
297
        private Direction facingRotation = Direction.NORTH;
298

299
        public State() {
300
            super(1);
3✔
301
        }
1✔
302

303
        @Override
304
        public void writeToNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
305
            super.writeToNBT(valueDeseralizationContext, tag);
4✔
306
            IValue value = getDisplayValue();
3✔
307
            if(value != null) {
2✔
308
                tag.putString("displayValueType", value.getType().getUniqueName().toString());
7✔
309
                tag.put("displayValue", ValueHelpers.serializeRaw(valueDeseralizationContext, value));
7✔
310
            }
311
            tag.putInt("facingRotation", facingRotation.ordinal());
6✔
312
        }
1✔
313

314
        @Override
315
        public void readFromNBT(ValueDeseralizationContext valueDeseralizationContext, CompoundTag tag) {
316
            super.readFromNBT(valueDeseralizationContext, tag);
×
317
            if(tag.contains("displayValueType", Tag.TAG_STRING)
×
318
                    && tag.contains("displayValue")) {
×
319
                IValueType valueType = ValueTypes.REGISTRY.getValueType(ResourceLocation.parse(tag.getString("displayValueType")));
×
320
                if(valueType != null) {
×
321
                    Tag serializedValue = tag.get("displayValue");
×
322
                    Component deserializationError = valueType.canDeserialize(valueDeseralizationContext, serializedValue);
×
323
                    if(deserializationError == null) {
×
324
                        setDisplayValue(ValueHelpers.deserializeRaw(valueDeseralizationContext, valueType, serializedValue));
×
325
                    } else {
326
                        IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, deserializationError.getString());
×
327
                    }
328
                } else {
×
329
                    IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR,
×
330
                            String.format("Tried to deserialize the value \"%s\" for type \"%s\" which could not be found.",
×
331
                                    tag.getString("displayValueType"), tag.getString("value")));
×
332
                }
333
            } else {
×
334
                setDisplayValue(null);
×
335
            }
336
            facingRotation = Direction.values()[Math.max(2, tag.getInt("facingRotation"))];
×
337
        }
×
338
    }
339

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