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

CyclopsMC / IntegratedDynamics / 22682705857

04 Mar 2026 06:10PM UTC coverage: 45.725% (-0.01%) from 45.735%
22682705857

Pull #1623

github

Copilot
Fix unsafe Level casts in BlockCable causing crash on sapling grow

Co-authored-by: rubensworks <440384+rubensworks@users.noreply.github.com>
Pull Request #1623: Fix ClassCastException in BlockCable when sapling grows nearby

2801 of 8854 branches covered (31.64%)

Branch coverage included in aggregate %.

12301 of 24174 relevant lines covered (50.89%)

2.43 hits per line

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

62.89
/src/main/java/org/cyclops/integrateddynamics/block/BlockCable.java
1
package org.cyclops.integrateddynamics.block;
2

3
import com.google.common.cache.Cache;
4
import com.google.common.cache.CacheBuilder;
5
import com.mojang.serialization.MapCodec;
6
import lombok.Setter;
7
import lombok.SneakyThrows;
8
import net.minecraft.core.BlockPos;
9
import net.minecraft.core.Direction;
10
import net.minecraft.server.level.ServerLevel;
11
import net.minecraft.util.RandomSource;
12
import net.minecraft.world.InteractionHand;
13
import net.minecraft.world.InteractionResult;
14
import net.minecraft.world.ItemInteractionResult;
15
import net.minecraft.world.entity.LivingEntity;
16
import net.minecraft.world.entity.player.Player;
17
import net.minecraft.world.item.ItemStack;
18
import net.minecraft.world.item.context.BlockPlaceContext;
19
import net.minecraft.world.level.*;
20
import net.minecraft.world.level.block.BaseEntityBlock;
21
import net.minecraft.world.level.block.Block;
22
import net.minecraft.world.level.block.RenderShape;
23
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
24
import net.minecraft.world.level.block.entity.BlockEntity;
25
import net.minecraft.world.level.block.entity.BlockEntityTicker;
26
import net.minecraft.world.level.block.entity.BlockEntityType;
27
import net.minecraft.world.level.block.state.BlockState;
28
import net.minecraft.world.level.block.state.StateDefinition;
29
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
30
import net.minecraft.world.level.block.state.properties.BooleanProperty;
31
import net.minecraft.world.level.material.Fluid;
32
import net.minecraft.world.level.material.FluidState;
33
import net.minecraft.world.level.material.Fluids;
34
import net.minecraft.world.phys.AABB;
35
import net.minecraft.world.phys.BlockHitResult;
36
import net.minecraft.world.phys.shapes.*;
37
import net.neoforged.neoforge.client.model.data.ModelProperty;
38
import net.neoforged.neoforge.common.extensions.ILevelExtension;
39
import org.cyclops.cyclopscore.block.BlockWithEntity;
40
import org.cyclops.cyclopscore.datastructure.EnumFacingMap;
41
import org.cyclops.cyclopscore.helper.BlockEntityHelpers;
42
import org.cyclops.integrateddynamics.Capabilities;
43
import org.cyclops.integrateddynamics.GeneralConfig;
44
import org.cyclops.integrateddynamics.RegistryEntries;
45
import org.cyclops.integrateddynamics.api.block.IDynamicLight;
46
import org.cyclops.integrateddynamics.api.block.IDynamicRedstone;
47
import org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;
48
import org.cyclops.integrateddynamics.api.part.IPartContainer;
49
import org.cyclops.integrateddynamics.api.part.IPartState;
50
import org.cyclops.integrateddynamics.api.part.IPartType;
51
import org.cyclops.integrateddynamics.api.part.PartRenderPosition;
52
import org.cyclops.integrateddynamics.block.shapes.*;
53
import org.cyclops.integrateddynamics.client.model.CableModel;
54
import org.cyclops.integrateddynamics.client.model.IRenderState;
55
import org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;
56
import org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;
57
import org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;
58
import org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;
59
import org.cyclops.integrateddynamics.core.helper.CableHelpers;
60
import org.cyclops.integrateddynamics.core.helper.NetworkHelpers;
61
import org.cyclops.integrateddynamics.core.helper.PartHelpers;
62

63
import javax.annotation.Nullable;
64
import java.util.Collection;
65
import java.util.Iterator;
66
import java.util.Map;
67
import java.util.Optional;
68
import java.util.concurrent.TimeUnit;
69

70
/**
71
 * A block that is built up from different parts.
72
 * This block refers to a ticking part entity.
73
 * @author rubensworks
74
 */
75
public class BlockCable extends BlockWithEntity implements SimpleWaterloggedBlock {
76

77
    public static final MapCodec<BlockCable> CODEC = simpleCodec(BlockCable::new);
3✔
78

79
    public static final float BLOCK_HARDNESS = 3.0F;
80

81
    public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
2✔
82

83
    // Model Properties
84
    public static final ModelProperty<Boolean> REALCABLE = new ModelProperty<>();
4✔
85
    public static final ModelProperty<Boolean>[] CONNECTED = new ModelProperty[6];
3✔
86
    public static final ModelProperty<PartRenderPosition>[] PART_RENDERPOSITIONS = new ModelProperty[6];
3✔
87
    public static final ModelProperty<Optional<BlockState>> FACADE = new ModelProperty<>();
4✔
88
    static {
89
        for(Direction side : Direction.values()) {
16✔
90
            CONNECTED[side.ordinal()] = new ModelProperty<>();
7✔
91
            PART_RENDERPOSITIONS[side.ordinal()] = new ModelProperty<>();
7✔
92
        }
93
    }
94
    public static final ModelProperty<IPartContainer> PARTCONTAINER = new ModelProperty<>();
4✔
95
    public static final ModelProperty<IRenderState> RENDERSTATE = new ModelProperty<>();
4✔
96

97
    // Collision boxes
98
    public final static AABB CABLE_CENTER_BOUNDINGBOX = new AABB(
10✔
99
            CableModel.MIN, CableModel.MIN, CableModel.MIN, CableModel.MAX, CableModel.MAX, CableModel.MAX);
100
    private final static EnumFacingMap<AABB> CABLE_SIDE_BOUNDINGBOXES = EnumFacingMap.forAllValues(
56✔
101
            new AABB(CableModel.MIN, 0, CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX), // DOWN
102
            new AABB(CableModel.MIN, CableModel.MAX, CableModel.MIN, CableModel.MAX, 1, CableModel.MAX), // UP
103
            new AABB(CableModel.MIN, CableModel.MIN, 0, CableModel.MAX, CableModel.MAX, CableModel.MIN), // NORTH
104
            new AABB(CableModel.MIN, CableModel.MAX, CableModel.MAX, CableModel.MAX, CableModel.MIN, 1), // SOUTH
105
            new AABB(0, CableModel.MIN, CableModel.MIN, CableModel.MIN, CableModel.MAX, CableModel.MAX), // WEST
106
            new AABB(CableModel.MAX, CableModel.MIN, CableModel.MIN, 1, CableModel.MAX, CableModel.MAX) // EAST
107
    );
108

109
    private final VoxelShapeComponentsFactory voxelShapeComponentsFactory = new VoxelShapeComponentsFactory(
31✔
110
            new VoxelShapeComponentsFactoryHandlerCableCenter(),
111
            new VoxelShapeComponentsFactoryHandlerCableConnections(),
112
            new VoxelShapeComponentsFactoryHandlerParts(),
113
            new VoxelShapeComponentsFactoryHandlerFacade()
114
    );
115

116
    @Setter
7✔
117
    private boolean disableCollisionBox = false;
118

119
    /**
120
     * Flag to skip expensive network initialization during bulk cable placement.
121
     * When true, onCableAdded calls are skipped in onPlace.
122
     * Network initialization should be done manually after all cables are placed.
123
     */
124
    public static boolean SKIP_NETWORK_INIT = false;
3✔
125

126
    public BlockCable(Properties properties) {
127
        super(properties, BlockEntityMultipartTicking::new);
4✔
128
        this.registerDefaultState(this.stateDefinition.any().setValue(WATERLOGGED, false));
11✔
129
    }
1✔
130

131
    @Override
132
    protected MapCodec<? extends BaseEntityBlock> codec() {
133
        return CODEC;
×
134
    }
135

136
    @Override
137
    public boolean useShapeForLightOcclusion(BlockState p_60576_) {
138
        return true;
2✔
139
    }
140

141
    @Override
142
    @Nullable
143
    public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState blockState, BlockEntityType<T> blockEntityType) {
144
        return level.isClientSide ? null : createTickerHelper(blockEntityType, RegistryEntries.BLOCK_ENTITY_MULTIPART_TICKING.get(), new BlockEntityMultipartTicking.Ticker<>());
12!
145
    }
146

147
    @Override
148
    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
149
        super.createBlockStateDefinition(builder);
3✔
150
        builder.add(WATERLOGGED);
9✔
151
    }
1✔
152

153
    @Override
154
    public BlockState updateShape(BlockState stateIn, Direction facing, BlockState facingState, LevelAccessor worldIn, BlockPos currentPos, BlockPos facingPos) {
155
        if (stateIn.getValue(WATERLOGGED)) {
6!
156
            worldIn.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(worldIn));
×
157
        }
158
        if (worldIn instanceof Level level) {
6!
159
            NetworkHelpers.onElementProviderBlockNeighborChange(level, currentPos, facingState.getBlock(), facing, facingPos);
7✔
160
        }
161
        return super.updateShape(stateIn, facing, facingState, worldIn, currentPos, facingPos);
9✔
162
    }
163

164
    @Override
165
    public BlockState getStateForPlacement(BlockPlaceContext context) {
166
        FluidState ifluidstate = context.getLevel().getFluidState(context.getClickedPos());
6✔
167
        return this.defaultBlockState().setValue(WATERLOGGED, ifluidstate.getType() == Fluids.WATER);
12!
168
    }
169

170
    @Override
171
    public FluidState getFluidState(BlockState state) {
172
        return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
14✔
173
    }
174

175
    @Override
176
    public boolean canPlaceLiquid(@org.jetbrains.annotations.Nullable Player player, BlockGetter worldIn, BlockPos pos, BlockState blockState, Fluid fluidIn) {
177
        return !blockState.getValue(BlockStateProperties.WATERLOGGED) && fluidIn == Fluids.WATER
8!
178
                && !(worldIn instanceof ILevelExtension levelExtension && CableHelpers.hasFacade(levelExtension, pos));
3!
179
    }
180

181
    @Override
182
    public void onBlockExploded(BlockState state, Level world, BlockPos blockPos, Explosion explosion) {
183
        CableHelpers.setRemovingCable(true);
2✔
184
        CableHelpers.onCableRemoving(world, blockPos, true, false, state);
7✔
185
        Collection<Direction> connectedCables = CableHelpers.getExternallyConnectedCables(world, blockPos);
4✔
186
        super.onBlockExploded(state, world, blockPos, explosion);
6✔
187
        CableHelpers.onCableRemoved(world, blockPos, connectedCables);
5✔
188
        CableHelpers.setRemovingCable(false);
2✔
189
    }
1✔
190

191
    @Override
192
    public boolean onDestroyedByPlayer(BlockState state, Level world, BlockPos pos, Player player, boolean willHarvest, FluidState fluid) {
193
        BlockRayTraceResultComponent rayTraceResult = getSelectedShape(state, world, pos, CollisionContext.of(player))
9✔
194
                .rayTrace(pos, player);
2✔
195
        if (rayTraceResult != null && rayTraceResult.getComponent().destroy(world, pos, player, false)) {
10!
196
            return false;
2✔
197
        }
198
        return rayTraceResult != null && super.onDestroyedByPlayer(state, world, pos, player, willHarvest, fluid);
13!
199
    }
200

201
    @Override
202
    public void onRemove(BlockState state, Level world, BlockPos blockPos, BlockState newState, boolean isMoving) {
203
        if (newState.getBlock() != this) {
4!
204
            Collection<Direction> connectedCables = null;
2✔
205
            if (!CableHelpers.isRemovingCable()) {
2✔
206
                CableHelpers.onCableRemoving(world, blockPos, false, false, state);
7✔
207
                connectedCables = CableHelpers.getExternallyConnectedCables(world, blockPos);
4✔
208
            }
209
            super.onRemove(state, world, blockPos, newState, isMoving);
7✔
210
            if (!CableHelpers.isRemovingCable() && !SKIP_NETWORK_INIT) {
4!
211
                CableHelpers.onCableRemoved(world, blockPos, connectedCables);
5✔
212
            }
213
        } else {
1✔
214
            super.onRemove(state, world, blockPos, newState, isMoving);
×
215
        }
216
    }
1✔
217

218
    @Override
219
    protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
220
        /*
221
            Wrench: sneak + right-click anywhere on cable to remove cable
222
                    right-click on a cable side to disconnect on that side
223
                    sneak + right-click on part to remove that part
224
            No wrench: right-click to open GUI
225
         */
226
        BlockEntityMultipartTicking tile = BlockEntityHelpers.get(pLevel, pPos, BlockEntityMultipartTicking.class).orElse(null);
8✔
227
        if(tile != null) {
2!
228
            BlockRayTraceResultComponent rayTraceResult = getSelectedShape(pState, pLevel, pPos, CollisionContext.of(pPlayer))
9✔
229
                    .rayTrace(pPos, pPlayer);
2✔
230
            if(rayTraceResult != null) {
2!
231
                InteractionResult actionResultType = rayTraceResult.getComponent().onBlockActivated(pState, pLevel, pPos, pPlayer, pHand, rayTraceResult);
10✔
232
                if (actionResultType.consumesAction()) {
3!
233
                    // TODO: in next major, return ItemInteractionResult in onBlockActivated so this workaround can be removed.
234
                    if (actionResultType == InteractionResult.SUCCESS) {
3!
235
                        return ItemInteractionResult.SUCCESS;
2✔
236
                    } else if (actionResultType == InteractionResult.CONSUME) {
×
237
                        return ItemInteractionResult.CONSUME;
×
238
                    } else if (actionResultType == InteractionResult.CONSUME_PARTIAL) {
×
239
                        return ItemInteractionResult.CONSUME_PARTIAL;
×
240
                    } else if (actionResultType == InteractionResult.SUCCESS_NO_ITEM_USED) {
×
241
                        return ItemInteractionResult.SUCCESS;
×
242
                    }
243
                    return ItemInteractionResult.SUCCESS;
×
244
                }
245
            }
246
        }
247
        return super.useItemOn(pStack, pState, pLevel, pPos, pPlayer, pHand, pHitResult);
×
248
    }
249

250
    @Override
251
    public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {
252
        super.onPlace(state, world, pos, oldState, isMoving);
7✔
253
        if (!world.isClientSide() && !SKIP_NETWORK_INIT) {
5!
254
            ICableFakeable cableFakeable = CableHelpers.getCableFakeable(world, pos, null).orElse(null);
8✔
255
            if (cableFakeable != null && cableFakeable.isRealCable()) {
5!
256
                CableHelpers.onCableAdded(world, pos);
3✔
257
            }
258
        }
259
    }
1✔
260

261
    @Override
262
    public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {
263
        super.setPlacedBy(world, pos, state, placer, itemStack);
7✔
264
        if (!world.isClientSide()) {
3!
265
            CableHelpers.onCableAddedByPlayer(world, pos, placer);
4✔
266
        }
267
    }
1✔
268

269
    @Override
270
    public ItemStack getCloneItemStack(BlockState state, net.minecraft.world.phys.HitResult target, LevelReader world,
271
                                  BlockPos blockPos, Player player) {
272
        BlockRayTraceResultComponent rayTraceResult = getSelectedShape(state, world, blockPos, CollisionContext.of(player))
×
273
                .rayTrace(blockPos, player);
×
274
        if(rayTraceResult != null && world instanceof Level level) {
×
275
            return rayTraceResult.getComponent().getCloneItemStack(level, blockPos);
×
276
        }
277
        return getCloneItemStack(world, blockPos, state);
×
278
    }
279

280
    @SuppressWarnings("deprecation")
281
    @Override
282
    public void neighborChanged(BlockState state, Level world, BlockPos pos, Block neighborBlock, BlockPos fromPos, boolean isMoving) {
283
        super.neighborChanged(state, world, pos, neighborBlock, fromPos, isMoving);
8✔
284
        NetworkHelpers.onElementProviderBlockNeighborChange(world, pos, neighborBlock, null, fromPos);
6✔
285
    }
1✔
286

287
    @Override
288
    public void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbor) {
289
        super.onNeighborChange(state, world, pos, neighbor);
6✔
290
        if (world instanceof Level level) {
6!
291
            NetworkHelpers.onElementProviderBlockNeighborChange(level, pos, world.getBlockState(neighbor).getBlock(), null, neighbor);
9✔
292
        }
293
    }
1✔
294

295
    @Override
296
    public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rand) {
297
        super.tick(state, world, pos, rand);
×
298
        BlockEntityHelpers.get(world, pos, BlockEntityMultipartTicking.class)
×
299
                .ifPresent(tile -> {
×
300
                    for (Map.Entry<Direction, PartHelpers.PartStateHolder<?, ?>> entry : tile
×
301
                            .getPartContainer().getPartData().entrySet()) {
×
302
                        updateTickPart(entry.getValue().getPart(), world, pos, entry.getValue().getState(), rand);
×
303
                    }
×
304
                });
×
305
    }
×
306

307
    protected void updateTickPart(IPartType partType, Level world, BlockPos pos, IPartState partState, RandomSource random) {
308
        partType.updateTick(world, pos, partState, random);
×
309
    }
×
310

311
    /* --------------- Start shapes and rendering --------------- */
312

313
    public AABB getCableBoundingBox(Direction side) {
314
        if (side == null) {
×
315
            return CABLE_CENTER_BOUNDINGBOX;
×
316
        } else {
317
            return CABLE_SIDE_BOUNDINGBOXES.get(side);
×
318
        }
319
    }
320

321
    public VoxelShapeComponents getSelectedShape(BlockState blockState, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
322
        return voxelShapeComponentsFactory.createShape(blockState, world, pos, selectionContext);
8✔
323
    }
324

325
    private final Cache<String, VoxelShape> CACHE_COLLISION_SHAPES = CacheBuilder.newBuilder()
4✔
326
            .expireAfterAccess(1, TimeUnit.MINUTES)
1✔
327
            .build();
2✔
328

329
    @SneakyThrows
×
330
    @Override
331
    public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
332
        VoxelShapeComponents selectedShape = getSelectedShape(state, world, pos, selectionContext);
7✔
333
        BlockRayTraceResultComponent rayTraceResult = selectedShape.rayTrace(pos, selectionContext instanceof EntityCollisionContext ? ((EntityCollisionContext) selectionContext).getEntity() : null);
11!
334
        if (rayTraceResult != null) {
2!
335
            return rayTraceResult.getComponent().getShape(state, world, pos, selectionContext);
×
336
        }
337

338
        String cableState = selectedShape.getStateId();
3✔
339

340
        // Cache the operations below, as they are too expensive to execute each render tick
341
        return CACHE_COLLISION_SHAPES.get(cableState, () -> {
8✔
342
            // Combine all VoxelShapes using IBooleanFunction.OR,
343
            // because for some reason our VoxelShapeComponents aggregator does not handle collisions properly.
344
            // This can probably be fixed, but I spent too much time on this already, and the current solution works just fine.
345
            Iterator<VoxelShape> it = selectedShape.iterator();
3✔
346
            if (!it.hasNext()) {
3✔
347
                return Shapes.empty();
2✔
348
            }
349
            VoxelShape shape = it.next();
4✔
350
            while (it.hasNext()) {
3✔
351
                shape = Shapes.join(shape, it.next(), BooleanOp.OR);
8✔
352
            }
353
            return shape.optimize();
3✔
354
        });
355
    }
356

357
    @Override
358
    public VoxelShape getCollisionShape(BlockState blockState, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
359
        return (disableCollisionBox || GeneralConfig.disableCableCollision) ? Shapes.empty() : super.getCollisionShape(blockState, world, pos, selectionContext);
12!
360
    }
361

362
    @Override
363
    public boolean hasDynamicShape() {
364
        return BlockCableConfig.dynamicShape;
2✔
365
    }
366

367
    @Override
368
    public int getLightBlock(BlockState blockState, BlockGetter world, BlockPos pos) {
369
        if (world instanceof Level level) {
6✔
370
            if (CableHelpers.isLightTransparent(level, pos, null, blockState)) {
6!
371
                return 0;
×
372
            }
373
            return CableHelpers.getFacade(level, pos, blockState)
8✔
374
                    .map(facade -> facade.getLightBlock(world, pos))
2✔
375
                    .orElse(0);
4✔
376
        }
377
        return 0;
2✔
378
    }
379

380
    @Override
381
    public RenderShape getRenderShape(BlockState blockState) {
382
        return RenderShape.MODEL;
×
383
    }
384

385
    @Override
386
    protected VoxelShape getBlockSupportShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
387
        return this.getShape(pState, pLevel, pPos, new CollisionContextBlockSupport());
9✔
388
    }
389

390
    @Override
391
    public boolean shouldDisplayFluidOverlay(BlockState state, BlockAndTintGetter world, BlockPos pos, FluidState fluidState) {
392
        return world instanceof ILevelExtension levelExtension && CableHelpers.getFacade(levelExtension, pos).isPresent();
×
393
    }
394

395
    /* --------------- Start IDynamicRedstone --------------- */
396

397
    @SuppressWarnings("deprecation")
398
    @Override
399
    public boolean isSignalSource(BlockState blockState) {
400
        return true;
×
401
    }
402

403
    @Override
404
    public boolean canConnectRedstone(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {
405
        if (world instanceof ILevelExtension levelExtension) {
6!
406
            if (side == null) {
2!
407
                for (Direction dummySide : Direction.values()) {
×
408
                    IDynamicRedstone dynamicRedstone = BlockEntityHelpers.getCapability(levelExtension, pos, dummySide, Capabilities.DynamicRedstone.BLOCK).orElse(null);
×
409
                    if (dynamicRedstone != null && (dynamicRedstone.getRedstoneLevel() >= 0 || dynamicRedstone.isAllowRedstoneInput())) {
×
410
                        return true;
×
411
                    }
412
                }
413
                return false;
×
414
            }
415
            IDynamicRedstone dynamicRedstone = BlockEntityHelpers.getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);
10✔
416
            return dynamicRedstone != null && (dynamicRedstone.getRedstoneLevel() >= 0 || dynamicRedstone.isAllowRedstoneInput());
12!
417
        }
418
        return false;
×
419
    }
420

421
    @SuppressWarnings("deprecation")
422
    @Override
423
    public int getDirectSignal(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {
424
        if (world instanceof ILevelExtension levelExtension) {
6!
425
            IDynamicRedstone dynamicRedstone = BlockEntityHelpers.getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);
10✔
426
            return dynamicRedstone != null && dynamicRedstone.isDirect() ? dynamicRedstone.getRedstoneLevel() : 0;
7!
427
        }
428
        return 0;
×
429
    }
430

431
    @SuppressWarnings("deprecation")
432
    @Override
433
    public int getSignal(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {
434
        if (world instanceof ILevelExtension levelExtension) {
6!
435
            IDynamicRedstone dynamicRedstone = BlockEntityHelpers.getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);
10✔
436
            return dynamicRedstone != null ? dynamicRedstone.getRedstoneLevel() : 0;
6!
437
        }
438
        return 0;
×
439
    }
440

441
    /* --------------- Start IDynamicLight --------------- */
442

443
    @Override
444
    public int getLightEmission(BlockState blockState, BlockGetter world, BlockPos pos) {
445
        int light = 0;
2✔
446
        if (world instanceof ILevelExtension levelExtension) {
6✔
447
            for (Direction side : Direction.values()) {
16✔
448
                IDynamicLight dynamicLight = levelExtension.getCapability(Capabilities.DynamicLight.BLOCK, pos, blockState, null, side);
9✔
449
                if (dynamicLight != null) {
2✔
450
                    light = Math.max(light, dynamicLight.getLightLevel());
5✔
451
                }
452
            }
453
        }
454
        return light;
2✔
455
    }
456

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