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

CyclopsMC / IntegratedDynamics / 19675908292

25 Nov 2025 04:02PM UTC coverage: 53.043% (+0.005%) from 53.038%
19675908292

push

github

rubensworks
Merge remote-tracking branch 'origin/master-1.21-lts' into master-1.21

2883 of 8780 branches covered (32.84%)

Branch coverage included in aggregate %.

17356 of 29376 relevant lines covered (59.08%)

3.07 hits per line

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

53.33
/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.network.RegistryFriendlyByteBuf;
10
import net.minecraft.network.chat.Component;
11
import net.minecraft.resources.ResourceLocation;
12
import net.minecraft.server.level.ServerPlayer;
13
import net.minecraft.world.InteractionHand;
14
import net.minecraft.world.InteractionResult;
15
import net.minecraft.world.MenuProvider;
16
import net.minecraft.world.entity.player.Inventory;
17
import net.minecraft.world.entity.player.Player;
18
import net.minecraft.world.inventory.AbstractContainerMenu;
19
import net.minecraft.world.item.ItemStack;
20
import net.minecraft.world.level.Level;
21
import net.minecraft.world.level.block.Block;
22
import net.minecraft.world.level.block.state.BlockBehaviour;
23
import net.minecraft.world.level.block.state.BlockState;
24
import net.minecraft.world.level.storage.ValueInput;
25
import net.minecraft.world.level.storage.ValueOutput;
26
import net.minecraft.world.phys.BlockHitResult;
27
import org.apache.commons.lang3.tuple.Triple;
28
import org.cyclops.cyclopscore.config.extendedconfig.BlockConfigCommon;
29
import org.cyclops.cyclopscore.helper.IModHelpers;
30
import org.cyclops.integrateddynamics.IntegratedDynamics;
31
import org.cyclops.integrateddynamics.api.evaluate.EvaluationException;
32
import org.cyclops.integrateddynamics.api.evaluate.variable.*;
33
import org.cyclops.integrateddynamics.api.network.INetwork;
34
import org.cyclops.integrateddynamics.api.network.IPartNetwork;
35
import org.cyclops.integrateddynamics.api.network.event.INetworkEvent;
36
import org.cyclops.integrateddynamics.api.part.IPartContainer;
37
import org.cyclops.integrateddynamics.api.part.IPartTypeActiveVariable;
38
import org.cyclops.integrateddynamics.api.part.PartPos;
39
import org.cyclops.integrateddynamics.api.part.PartTarget;
40
import org.cyclops.integrateddynamics.core.block.IgnoredBlock;
41
import org.cyclops.integrateddynamics.core.block.IgnoredBlockStatus;
42
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueHelpers;
43
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeList;
44
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
45
import org.cyclops.integrateddynamics.core.helper.L10NValues;
46
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
47
import org.cyclops.integrateddynamics.core.helper.PartHelpers;
48
import org.cyclops.integrateddynamics.core.helper.WrenchHelpers;
49
import org.cyclops.integrateddynamics.core.network.event.VariableContentsUpdatedEvent;
50
import org.cyclops.integrateddynamics.core.part.PartStateActiveVariableBase;
51
import org.cyclops.integrateddynamics.core.part.PartTypeBase;
52
import org.cyclops.integrateddynamics.inventory.container.ContainerPartPanelVariableDriven;
53

54
import javax.annotation.Nullable;
55
import java.util.List;
56
import java.util.Map;
57
import java.util.Optional;
58

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

65
    public PartTypePanelVariableDriven(String name) {
66
        super(name);
3✔
67
    }
1✔
68

69
    @Override
70
    protected Block createBlock(BlockConfigCommon<?> blockConfig, BlockBehaviour.Properties properties) {
71
        return new IgnoredBlockStatus(properties);
×
72
    }
73

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

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

96
    @Override
97
    public boolean isUpdate(S state) {
98
        return true;
2✔
99
    }
100

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

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

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

149
    @Override
150
    public boolean hasActiveVariable(IPartNetwork network, PartTarget target, S partState) {
151
        return partState.hasVariable();
×
152
    }
153

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

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

185
    @Override
186
    public Optional<MenuProvider> getContainerProvider(PartPos pos) {
187
        return Optional.of(new MenuProvider() {
×
188
            @Override
189
            public Component getDisplayName() {
190
                return Component.translatable(getTranslationKey());
×
191
            }
192

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

202
            @Override
203
            public boolean shouldTriggerClientSideContainerClosingOnOpen() {
204
                return false;
×
205
            }
206
        });
207
    }
208

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

216
        super.writeExtraGuiData(packetBuffer, pos, player);
×
217
    }
×
218

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

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

241
    protected void onVariableContentsUpdated(IPartNetwork network, PartTarget target, S state) {
242
        state.onVariableContentsUpdated((P) this, target);
4✔
243
    }
1✔
244

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

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

280
    @Override
281
    public boolean shouldTriggerBlockRenderUpdate(@Nullable S oldPartState, @Nullable S newPartState) {
282
        return super.shouldTriggerBlockRenderUpdate(oldPartState, newPartState)
×
283
                || getStatus(oldPartState) != getStatus(newPartState);
×
284
    }
285

286
    public static abstract class State<P extends PartTypePanelVariableDriven<P, S>, S extends PartTypePanelVariableDriven.State<P, S>> extends PartStateActiveVariableBase<P> {
287

288
        @Getter
3✔
289
        @Setter
4✔
290
        private IValue displayValue;
291
        @Getter
3✔
292
        @Setter
×
293
        private Direction facingRotation = Direction.NORTH;
294

295
        public State() {
296
            super(1);
3✔
297
        }
1✔
298

299
        @Override
300
        public void serialize(ValueOutput valueOutput) {
301
            super.serialize(valueOutput);
3✔
302
            IValue value = getDisplayValue();
3✔
303
            if(value != null) {
2✔
304
                valueOutput.putString("displayValueType", value.getType().getUniqueName().toString());
7✔
305
                ValueHelpers.serializeRaw(valueOutput.child("displayValue"), value);
5✔
306
            }
307
            valueOutput.putInt("facingRotation", facingRotation.ordinal());
6✔
308
        }
1✔
309

310
        @Override
311
        public void deserialize(ValueInput valueInput) {
312
            super.deserialize(valueInput);
3✔
313
            valueInput.getString("displayValueType")
8✔
314
                    .ifPresentOrElse(displayValueType -> {
1✔
315
                        IValueType valueType = ValueTypes.REGISTRY.getValueType(ResourceLocation.parse(displayValueType));
5✔
316
                        if(valueType != null) {
2!
317
                            ValueInput serializedValue = valueInput.child("displayValue").orElseThrow();
6✔
318
                            Component deserializationError = valueType.canDeserialize(serializedValue);
4✔
319
                            if(deserializationError == null) {
2!
320
                                setDisplayValue(ValueHelpers.deserializeRaw(serializedValue, valueType));
6✔
321
                            } else {
322
                                IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR, deserializationError.getString());
×
323
                            }
324
                        } else {
1✔
325
                            IntegratedDynamics.clog(org.apache.logging.log4j.Level.ERROR,
×
326
                                    String.format("Tried to deserialize the value \"%s\" for type \"%s\" which could not be found.",
×
327
                                            valueInput.getString("displayValueType"), valueInput.getString("value")));
×
328
                        }
329
                    }, () -> {
1✔
330
                        setDisplayValue(null);
3✔
331
                    });
1✔
332
            facingRotation = Direction.values()[Math.max(2, valueInput.getInt("facingRotation").orElseThrow())];
12✔
333
        }
1✔
334
    }
335

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