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

CyclopsMC / IntegratedDynamics / 21104370479

18 Jan 2026 02:08AM UTC coverage: 45.176% (-0.008%) from 45.184%
21104370479

Pull #1594

github

web-flow
Merge 51f5da90d into 506a005e1
Pull Request #1594: Fix part capabilities not being invalidated when network is reinitialized

2633 of 8602 branches covered (30.61%)

Branch coverage included in aggregate %.

11924 of 23621 relevant lines covered (50.48%)

2.4 hits per line

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

80.71
/src/main/java/org/cyclops/integrateddynamics/core/helper/CableHelpers.java
1
package org.cyclops.integrateddynamics.core.helper;
2

3
import com.google.common.collect.Lists;
4
import com.google.common.collect.Sets;
5
import net.minecraft.core.BlockPos;
6
import net.minecraft.core.Direction;
7
import net.minecraft.world.InteractionResult;
8
import net.minecraft.world.entity.LivingEntity;
9
import net.minecraft.world.entity.player.Player;
10
import net.minecraft.world.item.ItemStack;
11
import net.minecraft.world.level.BlockGetter;
12
import net.minecraft.world.level.Level;
13
import net.minecraft.world.level.block.Block;
14
import net.minecraft.world.level.block.state.BlockState;
15
import net.neoforged.neoforge.common.NeoForge;
16
import net.neoforged.neoforge.common.extensions.ILevelExtension;
17
import org.cyclops.cyclopscore.helper.BlockEntityHelpers;
18
import org.cyclops.cyclopscore.helper.IModHelpers;
19
import org.cyclops.cyclopscore.helper.ItemStackHelpers;
20
import org.cyclops.integrateddynamics.Capabilities;
21
import org.cyclops.integrateddynamics.api.block.IFacadeable;
22
import org.cyclops.integrateddynamics.api.block.cable.ICable;
23
import org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;
24
import org.cyclops.integrateddynamics.api.network.INetwork;
25
import org.cyclops.integrateddynamics.api.network.INetworkCarrier;
26
import org.cyclops.integrateddynamics.api.network.INetworkElement;
27
import org.cyclops.integrateddynamics.api.network.INetworkElementProvider;
28
import org.cyclops.integrateddynamics.api.part.IPartContainer;
29
import org.cyclops.integrateddynamics.api.part.IPartType;
30
import org.cyclops.integrateddynamics.api.path.IPathElement;
31
import org.cyclops.integrateddynamics.capability.facadeable.FacadeableTileMultipartTicking;
32
import org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;
33
import org.cyclops.integrateddynamics.core.network.event.NetworkInitializedEvent;
34
import org.cyclops.integrateddynamics.item.ItemBlockCable;
35

36
import javax.annotation.Nullable;
37
import java.lang.ref.WeakReference;
38
import java.util.Collection;
39
import java.util.List;
40
import java.util.Map;
41
import java.util.Optional;
42

43
/**
44
 * Helpers related to cables.
45
 * @author rubensworks
46
 */
47
public class CableHelpers {
×
48

49
    public static final Collection<Direction> ALL_SIDES = Sets.newIdentityHashSet();
2✔
50
    static {
51
        for (Direction side : Direction.values()) {
16✔
52
            ALL_SIDES.add(side);
4✔
53
        }
54
    }
55

56
    /**
57
     * Get the cable capability at the given position.
58
     * @param world The world.
59
     * @param pos The position.
60
     * @param side The side.
61
     * @return The optional cable capability.
62
     */
63
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
64
    public static Optional<ICable> getCable(ILevelExtension world, BlockPos pos, @Nullable Direction side) {
65
        return BlockEntityHelpers.getCapability(world, pos, side, Capabilities.Cable.BLOCK);
6✔
66
    }
67

68
    /**
69
     * Get the cable capability at the given position.
70
     * @param world The world.
71
     * @param pos The position.
72
     * @param side The side.
73
     * @param blockState The block state.
74
     * @return The optional cable capability.
75
     */
76
    public static Optional<ICable> getCable(ILevelExtension world, BlockPos pos, @Nullable Direction side, @Nullable BlockState blockState) {
77
        return Optional.ofNullable(world.getCapability(Capabilities.Cable.BLOCK, pos, blockState, null, side));
10✔
78
    }
79

80
    /**
81
     * Get the fakeable cable capability at the given position.
82
     * @param world The world.
83
     * @param pos The position.
84
     * @param side The side.
85
     * @return The optional fakeable cable capability.
86
     */
87
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
88
    public static Optional<ICableFakeable> getCableFakeable(ILevelExtension world, BlockPos pos, @Nullable Direction side) {
89
        return BlockEntityHelpers.getCapability(world, pos, side, Capabilities.CableFakeable.BLOCK);
6✔
90
    }
91

92
    /**
93
     * Get the fakeable cable capability at the given position.
94
     * @param world The world.
95
     * @param pos The position.
96
     * @param side The side.
97
     * @param blockState The block state.
98
     * @return The optional fakeable cable capability.
99
     */
100
    public static Optional<ICableFakeable> getCableFakeable(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {
101
        return Optional.ofNullable(world.getCapability(Capabilities.CableFakeable.BLOCK, pos, blockState, null, side));
10✔
102
    }
103

104
    /**
105
     * Get the path element capability at the given position.
106
     * @param world The world.
107
     * @param pos The position.
108
     * @param side The side.
109
     * @return The optional path element capability.
110
     */
111
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
112
    public static Optional<IPathElement> getPathElement(ILevelExtension world, BlockPos pos, @Nullable Direction side) {
113
        return BlockEntityHelpers.getCapability(world, pos, side, Capabilities.PathElement.BLOCK);
×
114
    }
115

116
    /**
117
     * Get the path element capability at the given position.
118
     * @param world The world.
119
     * @param pos The position.
120
     * @param side The side.
121
     * @return The optional path element capability.
122
     */
123
    public static Optional<IPathElement> getPathElement(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {
124
        return Optional.ofNullable(world.getCapability(Capabilities.PathElement.BLOCK, pos, blockState, null, side));
10✔
125
    }
126

127
    /**
128
     * Request to update the cable connections of all neighbours of the given position.
129
     * @param world The world.
130
     * @param pos The center position.
131
     * @param sides The sides to update connections for.
132
     */
133
    public static void updateConnectionsNeighbours(ILevelExtension world, BlockPos pos, Collection<Direction> sides) {
134
        for(Direction side : sides) {
10✔
135
            updateConnections(world, pos.relative(side), side.getOpposite());
7✔
136
        }
1✔
137
    }
1✔
138

139
    /**
140
     * Request to update the cable connections at the given position.
141
     * @param world The world.
142
     * @param pos The position.
143
     * @param side The side.
144
     */
145
    public static void updateConnections(ILevelExtension world, BlockPos pos, @Nullable Direction side) {
146
        getCable(world, pos, side)
5✔
147
                .ifPresent(ICable::updateConnections);
1✔
148
    }
1✔
149

150
    /**
151
     * Check if there is a cable at the given position AND if it is connected for the given side.
152
     * @param world The world.
153
     * @param pos The position.
154
     * @param side The side to check a connection for.
155
     * @return If there is a cable that is connected.
156
     */
157
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
158
    public static boolean isCableConnected(ILevelExtension world, BlockPos pos, Direction side) {
159
        return getCable(world, pos, side)
×
160
                .map(cable -> cable.isConnected(side))
×
161
                .orElse(false);
×
162
    }
163

164
    /**
165
     * Check if there is a cable at the given position AND if it is connected for the given side.
166
     * @param world The world.
167
     * @param pos The position.
168
     * @param side The side to check a connection for.
169
     * @param blockState The block state.
170
     * @return If there is a cable that is connected.
171
     */
172
    public static boolean isCableConnected(ILevelExtension world, BlockPos pos, Direction side, BlockState blockState) {
173
        return getCable(world, pos, side, blockState)
8✔
174
                .map(cable -> cable.isConnected(side))
7✔
175
                .orElse(false);
4✔
176
    }
177

178
    /**
179
     * Check if one side of the given cable at the given position can connect to the given side.
180
     * To be used when the cable connections are being updated.
181
     * This will check if the origin cable can connect to that side,
182
     * if there is a cable at the target side and if that cable can connect with this side.
183
     * This ignores any current cable connections.
184
     * @param world The world.
185
     * @param pos The center position.
186
     * @param side The side from the center position to check.
187
     * @param originCable The cable at the center position.
188
     * @return If it can connect.
189
     */
190
    public static boolean canCableConnectTo(ILevelExtension world, BlockPos pos, Direction side, ICable originCable) {
191
        BlockPos neighbourPos = pos.relative(side);
4✔
192
        return getCable(world, neighbourPos, side.getOpposite())
9✔
193
                .map(neighbourCable -> originCable.canConnect(neighbourCable, side)
12✔
194
                        && neighbourCable.canConnect(originCable, side.getOpposite()))
6✔
195
                .orElse(false);
4✔
196
    }
197

198
    /**
199
     * Check if the given position is not a fake cable.
200
     * This can mean that there is no cable at all!
201
     * But if there is a cable, this method will return true only if it is a real cable.
202
     * @param world The world.
203
     * @param pos The position.
204
     * @param side The side.
205
     * @return If there is no fake cable.
206
     */
207
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
208
    public static boolean isNoFakeCable(ILevelExtension world, BlockPos pos, @Nullable Direction side) {
209
        return getCableFakeable(world, pos, side)
6✔
210
                .map(ICableFakeable::isRealCable)
2✔
211
                .orElse(true);
4✔
212
    }
213

214
    /**
215
     * Check if the given position is not a fake cable.
216
     * This can mean that there is no cable at all!
217
     * But if there is a cable, this method will return true only if it is a real cable.
218
     * @param world The world.
219
     * @param pos The position.
220
     * @param side The side.
221
     * @param blockState The block state.
222
     * @return If there is no fake cable.
223
     */
224
    public static boolean isNoFakeCable(ILevelExtension world, BlockPos pos, @Nullable Direction side, BlockState blockState) {
225
        return getCableFakeable(world, pos, side, blockState)
7✔
226
                .map(ICableFakeable::isRealCable)
2✔
227
                .orElse(true);
4✔
228
    }
229

230
    /**
231
     * Disconnect a cable's side.
232
     * @param world The cable world.
233
     * @param pos The cable position.
234
     * @param side The cable side.
235
     * @param cable The cable to disconnect.
236
     * @param disconnectSide The side to disconnect.
237
     */
238
    public static void disconnectCable(Level world, BlockPos pos, Direction side, ICable cable, Direction disconnectSide) {
239
        // Store the disconnection in the part entity
240
        cable.disconnect(disconnectSide);
3✔
241

242
        // Signal changes
243
        cable.updateConnections();
2✔
244
        Collection<Direction> sidesToUpdate = getCableConnections(cable);
3✔
245
        sidesToUpdate.add(disconnectSide);
4✔
246
        CableHelpers.updateConnectionsNeighbours(world, pos, sidesToUpdate);
4✔
247

248
        // Reinit the networks for this block and the disconnected neighbour.
249
        NetworkHelpers.initNetwork(world, pos, side);
5✔
250
        NetworkHelpers.initNetwork(world, pos.relative(disconnectSide), side.getOpposite());
8✔
251
    }
1✔
252

253
    /**
254
     * Actions to be performed when a player right clicks on a cable.
255
     * @param world The world  of the cable.
256
     * @param pos The position of the cable.
257
     * @param state The blockstate of the cable.
258
     * @param player The player activating the cable.
259
     * @param heldItem The item with which the player is right-clicking.
260
     * @param side The side of the block the player right-clicked on.
261
     * @param cableConnectionHit The side identifying the cable connection that is being activated,
262
     *                           this will be null if the center part of the cable is activated.
263
     * @return Action result.
264
     */
265
    public static InteractionResult onCableActivated(Level world, BlockPos pos, BlockState state, Player player,
266
                                                    ItemStack heldItem, Direction side, @Nullable Direction cableConnectionHit) {
267
        ICable cable = CableHelpers.getCable(world, pos, side).orElse(null);
8✔
268
        if (cable == null) {
2!
269
            return InteractionResult.PASS;
×
270
        }
271

272
        if(WrenchHelpers.isWrench(player, heldItem, world, pos, side)) {
7!
273
            if (world.isClientSide()) {
3!
274
                return InteractionResult.SUCCESS; // Don't do anything client-side
×
275
            }
276
            if (player.isSecondaryUseActive()) {
3✔
277
                removeCable(world, pos, player);
5✔
278
            } else if (cableConnectionHit != null) {
2✔
279
                disconnectCable(world, pos, side, cable, cableConnectionHit);
7✔
280
            } else if (cableConnectionHit == null) {
2!
281
                // Reconnect cable side
282
                BlockPos neighbourPos = pos.relative(side);
4✔
283
                ICable neighbourCable = CableHelpers.getCable(world, neighbourPos, side.getOpposite()).orElse(null);
9✔
284
                if(neighbourCable != null && !cable.isConnected(side) &&
9!
285
                        (cable.canConnect(neighbourCable, side) || neighbourCable.canConnect(cable, side.getOpposite()))
8!
286
                        ) {
287
                    // Notify the reconnection in the part entity of this and the neighbour block,
288
                    // since we don't know in which one the disconnection was made.
289
                    cable.reconnect(side);
3✔
290
                    neighbourCable.reconnect(side.getOpposite());
4✔
291

292
                    // Signal changes
293
                    cable.updateConnections();
2✔
294
                    Collection<Direction> sidesToUpdate = getCableConnections(cable);
3✔
295
                    sidesToUpdate.add(side);
4✔
296
                    CableHelpers.updateConnectionsNeighbours(world, pos, sidesToUpdate);
4✔
297

298
                    // Reinit the networks for this block and the connected neighbour.
299
                    NetworkHelpers.initNetwork(world, pos, side);
5✔
300
                    NetworkHelpers.initNetwork(world, neighbourPos, side.getOpposite());
6✔
301
                }
302
            }
303
            return InteractionResult.SUCCESS;
2✔
304
        }
305
        return InteractionResult.PASS;
×
306
    }
307

308
    private static WeakReference<LivingEntity> CABLE_PLACER_SNAPSHOT = new WeakReference<>(null);
5✔
309

310
    /**
311
     * This should be called when a cable is added.
312
     * This method automatically notifies the neighbours and (re-)initializes the network if this cable carries one.
313
     * This should in most cases only be called server-side.
314
     * @param world The world.
315
     * @param pos The position.
316
     */
317
    public static void onCableAdded(Level world, BlockPos pos) {
318
        LivingEntity placer = CABLE_PLACER_SNAPSHOT.get();
4✔
319
        if (placer != null) {
2✔
320
            CableHelpers.onCableAddedByPlayer(world, pos, placer);
4✔
321
            CABLE_PLACER_SNAPSHOT = new WeakReference<>(null);
6✔
322
        } else {
323
            onCableAddedByPlayerActual(world, pos, null);
4✔
324
        }
325
    }
1✔
326

327
    /**
328
     * This should be called when a cable was added by a player.
329
     * This should be called after {@link CableHelpers#onCableAdded(Level, BlockPos)}.
330
     * It simply emits an player-sensitive init event on the network bus.
331
     * @param world The world.
332
     * @param pos The position.
333
     * @param placer The entity who placed the cable.
334
     */
335
    public static void onCableAddedByPlayer(Level world, BlockPos pos, @Nullable LivingEntity placer) {
336
        if (world.captureBlockSnapshots) {
3✔
337
            CABLE_PLACER_SNAPSHOT = new WeakReference<>(placer);
6✔
338
        } else {
339
            onCableAddedByPlayerActual(world, pos, placer);
4✔
340
        }
341
    }
1✔
342

343
    /**
344
     * This should be called when a cable was added by a player.
345
     * This should be called after {@link CableHelpers#onCableAdded(Level, BlockPos)}.
346
     * It simply emits an player-sensitive init event on the network bus.
347
     * @param world The world.
348
     * @param pos The position.
349
     * @param placer The entity who placed the cable.
350
     */
351
    public static void onCableAddedByPlayerActual(Level world, BlockPos pos, @Nullable LivingEntity placer) {
352
        CableHelpers.updateConnectionsNeighbours(world, pos, CableHelpers.ALL_SIDES);
4✔
353
        if(!world.isClientSide()) {
3!
354
            NetworkHelpers.initNetwork(world, pos, null)
8✔
355
                    .ifPresent(network -> NeoForge.EVENT_BUS.post(new NetworkInitializedEvent(network, world, pos, placer)));
12✔
356
        }
357
    }
1✔
358

359
    /**
360
     * This should be called when a cable is being removed, while the part entity is still present.
361
     * This method won't do anything when called client-side.
362
     * @param world The world.
363
     * @param pos The position.
364
     * @param dropMainElement If the main part element should be dropped.
365
     * @param saveState If the element state should be saved in the item.
366
     * @return If the cable was removed from the network.
367
     */
368
    public static boolean onCableRemoving(Level world, BlockPos pos, boolean dropMainElement, boolean saveState, BlockState blockState) {
369
        if (!world.isClientSide() && CableHelpers.isNoFakeCable(world, pos, null)) {
8!
370
            INetworkCarrier networkCarrier = NetworkHelpers.getNetworkCarrier(world, pos, null, blockState).orElse(null);
9✔
371

372
            // Get all drops from the network elements this cable provides.
373
            List<ItemStack> itemStacks = Lists.newLinkedList();
2✔
374
            INetworkElementProvider networkElementProvider = NetworkHelpers.getNetworkElementProvider(world, pos, null, blockState).orElse(null);
9✔
375
            if (networkElementProvider != null) {
2!
376
                for (INetworkElement networkElement : networkElementProvider.createNetworkElements(world, pos)) {
13✔
377
                    networkElement.addDrops(blockState, itemStacks, dropMainElement, saveState);
6✔
378
                }
1✔
379
                for (ItemStack itemStack : itemStacks) {
10✔
380
                    Block.popResource(world, pos, itemStack);
4✔
381
                }
1✔
382
            }
383

384
            // If the cable has a network, remove it from the network.
385
            if(networkCarrier != null && networkCarrier.getNetwork() != null) {
5!
386
                IPathElement pathElement = getPathElement(world, pos, null, blockState)
6✔
387
                        .orElseThrow(() -> new IllegalStateException("Could not find a valid path element capability"));
3✔
388
                INetwork network = networkCarrier.getNetwork();
3✔
389
                networkCarrier.setNetwork(null);
3✔
390
                world.invalidateCapabilities(pos);
3✔
391
                return network.removePathElement(pathElement, null, blockState);
6✔
392
            }
393
        }
394
        return true;
2✔
395
    }
396

397
    /**
398
     * This should be called AFTER a cable is removed, at this stage the part entity will not exist anymore.
399
     * This method won't do anything when called client-side.
400
     * @param world The world.
401
     * @param pos The position.
402
     * @param sides The sides to update connections for.
403
     * @return If the cable was removed from the network.
404
     */
405
    public static boolean onCableRemoved(Level world, BlockPos pos, Collection<Direction> sides) {
406
        updateConnectionsNeighbours(world, pos, sides);
4✔
407
        if (!world.isClientSide()) {
3!
408
            // Reinit neighbouring networks.
409
            for(Direction side : sides) {
10✔
410
                BlockPos sidePos = pos.relative(side);
4✔
411
                NetworkHelpers.initNetwork(world, sidePos, side.getOpposite());
6✔
412
            }
1✔
413
        }
414
        return true;
2✔
415
    }
416

417
    private static boolean removingCable = false;
3✔
418
    /**
419
     * @return If {@link #removeCable} is currently being called.
420
     */
421
    public static boolean isRemovingCable() {
422
        return removingCable;
2✔
423
    }
424
    /**
425
     * @param removingCable If the removing cable flag should be set
426
     */
427
    public static void setRemovingCable(boolean removingCable) {
428
        CableHelpers.removingCable = removingCable;
2✔
429
    }
1✔
430

431
    /**
432
     * Remove a cable.
433
     * This will automatically handle sounds, drops,
434
     * fakeable cables, network element removal and network (re)intialization.
435
     * @param world The world.
436
     * @param pos The position.
437
     * @param player The player removing the cable or null.
438
     */
439
    public static void removeCable(Level world, BlockPos pos, @Nullable Player player) {
440
        removingCable = true;
2✔
441
        ICable cable = getCable(world, pos, null).orElse(null);
8✔
442
        ICableFakeable cableFakeable = getCableFakeable(world, pos, null).orElse(null);
8✔
443
        IPartContainer partContainer = PartHelpers.getPartContainer(world, pos, null).orElse(null);
8✔
444
        BlockState blockState = world.getBlockState(pos);
4✔
445
        if (cable == null) {
2!
446
            removingCable = false;
×
447
            return;
×
448
        }
449

450
        Collection<Direction> connectedCables = getCableConnections(cable);
3✔
451
        CableHelpers.onCableRemoving(world, pos, false, false, blockState);
7✔
452
        // If the cable has no parts or is not fakeable, remove the block,
453
        // otherwise mark the cable as being fake.
454
        if (cableFakeable == null || partContainer == null || !partContainer.hasParts()) {
7!
455
            cable.destroy();
3✔
456
        } else {
457
            cableFakeable.setRealCable(false);
3✔
458
        }
459
        if (player == null) {
2!
460
            ItemStackHelpers.spawnItemStack(world, pos, cable.getItemStack());
×
461
        } else if (!player.isCreative()) {
3!
462
            ItemStackHelpers.spawnItemStackToPlayer(world, pos, cable.getItemStack(), player);
6✔
463
        }
464
        CableHelpers.onCableRemoved(world, pos, connectedCables);
5✔
465

466
        ItemBlockCable.playBreakSound(world, pos, blockState);
4✔
467

468
        removingCable = false;
2✔
469
    }
1✔
470

471
    /**
472
     * Check if the target has a facade.
473
     * @param world The world.
474
     * @param pos The position.
475
     * @return If it has a facade.
476
     */
477
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
478
    public static boolean hasFacade(ILevelExtension world, BlockPos pos) {
479
        return BlockEntityHelpers.getCapability(world, pos, null, Capabilities.Facadeable.BLOCK)
×
480
                .map(IFacadeable::hasFacade)
×
481
                .orElse(false);
×
482
    }
483

484
    /**
485
     * Check if the target has a facade.
486
     * @param world The world.
487
     * @param pos The position.
488
     * @param blockState The block state.
489
     * @return If it has a facade.
490
     */
491
    public static boolean hasFacade(ILevelExtension world, BlockPos pos, BlockState blockState) {
492
        return Optional.ofNullable(world.getCapability(Capabilities.Facadeable.BLOCK, pos, blockState, null, null))
11✔
493
                .map(IFacadeable::hasFacade)
2✔
494
                .orElse(false);
4✔
495
    }
496

497
    /**
498
     * Get the target's facade
499
     * @param world The world.
500
     * @param pos The position.
501
     * @return The optional facade.
502
     */
503
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
504
    public static Optional<BlockState> getFacade(ILevelExtension world, BlockPos pos) {
505
        return BlockEntityHelpers.getCapability(world, pos, null, Capabilities.Facadeable.BLOCK)
7✔
506
                .flatMap(facadeable -> Optional.ofNullable(facadeable.getFacade()));
5✔
507
    }
508

509
    /**
510
     * Get the target's facade
511
     * @param world The world.
512
     * @param pos The position.
513
     * @param blockState The block state.
514
     * @return The optional facade.
515
     */
516
    public static Optional<BlockState> getFacade(ILevelExtension world, BlockPos pos, BlockState blockState) {
517
        return Optional.ofNullable(world.getCapability(Capabilities.Facadeable.BLOCK, pos, blockState, null, null))
11✔
518
                .flatMap(facadeable -> Optional.ofNullable(facadeable.getFacade()));
5✔
519
    }
520

521
    /**
522
     * Get the target's facade from {@link BlockEntityMultipartTicking} without going through capabilities.
523
     * This is necessary because some BlockColor's rely on BlockGetter, which do not support capabilities.
524
     * @param world The world.
525
     * @param pos The position.
526
     * @return The optional facade.
527
     */
528
    public static Optional<BlockState> getFacadeMultipartTicking(BlockGetter world, BlockPos pos) {
529
        return IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityMultipartTicking.class)
×
530
                .flatMap(blockEntity -> Optional.ofNullable(new FacadeableTileMultipartTicking(blockEntity).getFacade()));
×
531
    }
532

533
    @Deprecated // TODO: attempt to migrate all calls to method with BlockState param
534
    public static boolean isLightTransparent(Level world, BlockPos pos, @Nullable Direction side) {
535
        return PartHelpers.getPartContainer(world, pos, side)
×
536
                .map(partContainer -> {
×
537
                    for (Map.Entry<Direction, IPartType<?, ?>> entry : partContainer.getParts().entrySet()) {
×
538
                        IPartType part = entry.getValue();
×
539
                        if (part.forceLightTransparency(partContainer.getPartState(entry.getKey()))) {
×
540
                            return true;
×
541
                        }
542
                    }
×
543
                    return false;
×
544
                })
545
                .orElse(false);
×
546
    }
547

548
    public static boolean isLightTransparent(Level world, BlockPos pos, @Nullable Direction side, BlockState blockState) {
549
        return PartHelpers.getPartContainer(world, pos, side, blockState)
7✔
550
                .map(partContainer -> {
2✔
551
                    for (Map.Entry<Direction, IPartType<?, ?>> entry : partContainer.getParts().entrySet()) {
12✔
552
                        IPartType part = entry.getValue();
4✔
553
                        if (part.forceLightTransparency(partContainer.getPartState(entry.getKey()))) {
8!
554
                            return true;
×
555
                        }
556
                    }
1✔
557
                    return false;
3✔
558
                })
559
                .orElse(false);
4✔
560
    }
561

562
    /**
563
     * Get the sides the cable is currently connected to.
564
     * @param cable A cable.
565
     * @return The cable connections.
566
     */
567
    public static Collection<Direction> getCableConnections(ICable cable) {
568
        Collection<Direction> sides = Sets.newIdentityHashSet();
2✔
569
        for (Direction side : Direction.values()) {
16✔
570
            if (cable.isConnected(side)) {
4✔
571
                sides.add(side);
4✔
572
            }
573
        }
574
        return sides;
2✔
575
    }
576

577
    /**
578
     * Get the sides that are externally connected to the given position.
579
     * @param world The world.
580
     * @param pos The position.
581
     * @return The sides.
582
     */
583
    public static Collection<Direction> getExternallyConnectedCables(Level world, BlockPos pos) {
584
        Collection<Direction> sides = Sets.newIdentityHashSet();
2✔
585
        for (Direction side : Direction.values()) {
16✔
586
            if (CableHelpers.isCableConnected(world, pos.relative(side), side.getOpposite(), null)) {
9✔
587
                sides.add(side);
4✔
588
            }
589
        }
590
        return sides;
2✔
591
    }
592
}
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