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

CyclopsMC / IntegratedDynamics / 22186773560

19 Feb 2026 02:52PM UTC coverage: 52.603% (+0.2%) from 52.363%
22186773560

push

github

web-flow
Remove Lombok dependency (#1604)

2911 of 8664 branches covered (33.6%)

Branch coverage included in aggregate %.

17683 of 30486 relevant lines covered (58.0%)

3.01 hits per line

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

62.35
/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 net.minecraft.core.BlockPos;
7
import net.minecraft.core.Direction;
8
import net.minecraft.server.level.ServerLevel;
9
import net.minecraft.util.RandomSource;
10
import net.minecraft.world.InteractionHand;
11
import net.minecraft.world.InteractionResult;
12
import net.minecraft.world.entity.LivingEntity;
13
import net.minecraft.world.entity.player.Player;
14
import net.minecraft.world.item.ItemStack;
15
import net.minecraft.world.item.context.BlockPlaceContext;
16
import net.minecraft.world.level.*;
17
import net.minecraft.world.level.block.BaseEntityBlock;
18
import net.minecraft.world.level.block.Block;
19
import net.minecraft.world.level.block.RenderShape;
20
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
21
import net.minecraft.world.level.block.entity.BlockEntity;
22
import net.minecraft.world.level.block.entity.BlockEntityTicker;
23
import net.minecraft.world.level.block.entity.BlockEntityType;
24
import net.minecraft.world.level.block.state.BlockState;
25
import net.minecraft.world.level.block.state.StateDefinition;
26
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
27
import net.minecraft.world.level.block.state.properties.BooleanProperty;
28
import net.minecraft.world.level.material.Fluid;
29
import net.minecraft.world.level.material.FluidState;
30
import net.minecraft.world.level.material.Fluids;
31
import net.minecraft.world.level.redstone.Orientation;
32
import net.minecraft.world.phys.AABB;
33
import net.minecraft.world.phys.BlockHitResult;
34
import net.minecraft.world.phys.shapes.*;
35
import net.neoforged.neoforge.common.extensions.ILevelExtension;
36
import net.neoforged.neoforge.model.data.ModelProperty;
37
import org.cyclops.cyclopscore.block.BlockWithEntity;
38
import org.cyclops.cyclopscore.datastructure.EnumFacingMap;
39
import org.cyclops.cyclopscore.helper.IModHelpers;
40
import org.cyclops.cyclopscore.helper.IModHelpersNeoForge;
41
import org.cyclops.integrateddynamics.Capabilities;
42
import org.cyclops.integrateddynamics.GeneralConfig;
43
import org.cyclops.integrateddynamics.RegistryEntries;
44
import org.cyclops.integrateddynamics.api.block.IDynamicLight;
45
import org.cyclops.integrateddynamics.api.block.IDynamicRedstone;
46
import org.cyclops.integrateddynamics.api.block.cable.ICableFakeable;
47
import org.cyclops.integrateddynamics.api.part.IPartContainer;
48
import org.cyclops.integrateddynamics.api.part.IPartState;
49
import org.cyclops.integrateddynamics.api.part.IPartType;
50
import org.cyclops.integrateddynamics.api.part.PartRenderPosition;
51
import org.cyclops.integrateddynamics.block.shapes.*;
52
import org.cyclops.integrateddynamics.client.model.CableModel;
53
import org.cyclops.integrateddynamics.client.model.IRenderState;
54
import org.cyclops.integrateddynamics.core.block.BlockRayTraceResultComponent;
55
import org.cyclops.integrateddynamics.core.block.VoxelShapeComponents;
56
import org.cyclops.integrateddynamics.core.block.VoxelShapeComponentsFactory;
57
import org.cyclops.integrateddynamics.core.blockentity.BlockEntityMultipartTicking;
58
import org.cyclops.integrateddynamics.core.helper.CableHelpers;
59
import org.cyclops.integrateddynamics.core.helper.Helpers;
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.Iterator;
65
import java.util.Map;
66
import java.util.Optional;
67
import java.util.concurrent.TimeUnit;
68

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

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

78
    public static final float BLOCK_HARDNESS = 3.0F;
79

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

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

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

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

115
    private boolean disableCollisionBox = false;
3✔
116

117
    public void setDisableCollisionBox(boolean disableCollisionBox) {
118
        this.disableCollisionBox = disableCollisionBox;
3✔
119
    }
1✔
120

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

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

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

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

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

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

155
    @Override
156
    protected BlockState updateShape(BlockState state, LevelReader level, ScheduledTickAccess scheduledTickAccess, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random) {
157
        if (level instanceof ServerLevel serverLevel) {
6!
158
            if (state.getValue(WATERLOGGED)) {
6!
159
                serverLevel.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
×
160
            }
161
            NetworkHelpers.onElementProviderBlockNeighborChange(serverLevel, pos, direction);
4✔
162
        }
163
        return super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random);
11✔
164
    }
165

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

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

177
    @Override
178
    public boolean canPlaceLiquid(@org.jetbrains.annotations.Nullable LivingEntity entity, BlockGetter worldIn, BlockPos pos, BlockState blockState, Fluid fluidIn) {
179
        return !blockState.getValue(BlockStateProperties.WATERLOGGED) && fluidIn == Fluids.WATER
×
180
                && !(worldIn instanceof ILevelExtension levelExtension && CableHelpers.hasFacade(levelExtension, pos, blockState));
×
181
    }
182

183
    @Override
184
    public void onBlockExploded(BlockState state, ServerLevel world, BlockPos blockPos, Explosion explosion) {
185
        CableHelpers.setRemovingCable(true);
2✔
186
        CableHelpers.onCableRemoving(world, blockPos, true, false, state, world.getBlockEntity(blockPos));
10✔
187
        super.onBlockExploded(state, world, blockPos, explosion);
6✔
188
        CableHelpers.onCableRemoved(world, blockPos);
4✔
189
        CableHelpers.setRemovingCable(false);
2✔
190
    }
1✔
191

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

202
    @Override
203
    protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {
204
        super.affectNeighborsAfterRemoval(state, level, pos, movedByPiston);
6✔
205

206
        if (!CableHelpers.isRemovingCable() && !SKIP_NETWORK_INIT) {
4!
207
            CableHelpers.onCableRemoved(level, pos);
4✔
208
        }
209
    }
1✔
210

211
    @Override
212
    protected InteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
213
        /*
214
            Wrench: sneak + right-click anywhere on cable to remove cable
215
                    right-click on a cable side to disconnect on that side
216
                    sneak + right-click on part to remove that part
217
            No wrench: right-click to open GUI
218
         */
219
        BlockEntityMultipartTicking tile = IModHelpers.get().getBlockEntityHelpers().get(pLevel, pPos, BlockEntityMultipartTicking.class).orElse(null);
10✔
220
        if(tile != null) {
2!
221
            BlockRayTraceResultComponent rayTraceResult = getSelectedShape(pState, pLevel, pPos, CollisionContext.of(pPlayer))
9✔
222
                    .rayTrace(pPos, pPlayer);
2✔
223
            if(rayTraceResult != null) {
2!
224
                InteractionResult actionResultType = rayTraceResult.getComponent().onBlockActivated(pState, pLevel, pPos, pPlayer, pHand, rayTraceResult);
10✔
225
                if (actionResultType.consumesAction()) {
3!
226
                    return actionResultType;
2✔
227
                }
228
            }
229
        }
230
        return super.useItemOn(pStack, pState, pLevel, pPos, pPlayer, pHand, pHitResult);
×
231
    }
232

233
    @Override
234
    public void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {
235
        super.onPlace(state, world, pos, oldState, isMoving);
7✔
236
        if (!world.isClientSide() && !SKIP_NETWORK_INIT) {
5!
237
            ICableFakeable cableFakeable = CableHelpers.getCableFakeable(world, pos, null).orElse(null);
8✔
238
            if (cableFakeable != null && cableFakeable.isRealCable()) {
5!
239
                CableHelpers.onCableAdded(world, pos);
3✔
240
            }
241
        }
242
    }
1✔
243

244
    @Override
245
    public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {
246
        super.setPlacedBy(world, pos, state, placer, itemStack);
7✔
247
        if (!world.isClientSide()) {
3!
248
            CableHelpers.onCableAddedByPlayer(world, pos, placer);
4✔
249
        }
250
    }
1✔
251

252
    @Override
253
    public ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData, Player player) {
254
        BlockRayTraceResultComponent rayTraceResult = getSelectedShape(state, level, pos, CollisionContext.of(player))
×
255
                .rayTrace(pos, player);
×
256
        if(rayTraceResult != null) {
×
257
            return rayTraceResult.getComponent().getCloneItemStack((Level) level, pos);
×
258
        }
259
        return super.getCloneItemStack(level, pos, state, includeData, player);
×
260
    }
261

262
    @Override
263
    protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @org.jetbrains.annotations.Nullable Orientation orientation, boolean movedByPiston) {
264
        super.neighborChanged(state, level, pos, neighborBlock, orientation, movedByPiston);
8✔
265
        NetworkHelpers.onElementProviderBlockNeighborChange(level, pos, orientation != null ? orientation.getFront().getOpposite() : null);
6!
266
    }
1✔
267

268
    @Override
269
    public void onNeighborChange(BlockState state, LevelReader world, BlockPos pos, BlockPos neighbor) {
270
        super.onNeighborChange(state, world, pos, neighbor);
6✔
271
        if (world instanceof Level level) {
6!
272
            NetworkHelpers.onElementProviderBlockNeighborChange(level, pos, null);
4✔
273
        }
274
    }
1✔
275

276
    @Override
277
    public void tick(BlockState state, ServerLevel world, BlockPos pos, RandomSource rand) {
278
        super.tick(state, world, pos, rand);
×
279
        IModHelpers.get().getBlockEntityHelpers().get(world, pos, BlockEntityMultipartTicking.class)
×
280
                .ifPresent(tile -> {
×
281
                    for (Map.Entry<Direction, PartHelpers.PartStateHolder<?, ?>> entry : tile
×
282
                            .getPartContainer().getPartData().entrySet()) {
×
283
                        updateTickPart(entry.getValue().getPart(), world, pos, entry.getValue().getState(), rand);
×
284
                    }
×
285
                });
×
286
    }
×
287

288
    protected void updateTickPart(IPartType partType, Level world, BlockPos pos, IPartState partState, RandomSource random) {
289
        partType.updateTick(world, pos, partState, random);
×
290
    }
×
291

292
    /* --------------- Start shapes and rendering --------------- */
293

294
    public AABB getCableBoundingBox(Direction side) {
295
        if (side == null) {
×
296
            return CABLE_CENTER_BOUNDINGBOX;
×
297
        } else {
298
            return CABLE_SIDE_BOUNDINGBOXES.get(side);
×
299
        }
300
    }
301

302
    public VoxelShapeComponents getSelectedShape(BlockState blockState, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
303
        return voxelShapeComponentsFactory.createShape(blockState, world, pos, selectionContext);
8✔
304
    }
305

306
    private final Cache<String, VoxelShape> CACHE_COLLISION_SHAPES = CacheBuilder.newBuilder()
4✔
307
            .expireAfterAccess(1, TimeUnit.MINUTES)
1✔
308
            .build();
2✔
309

310
    @Override
311
    public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
312
        VoxelShapeComponents selectedShape = getSelectedShape(state, world, pos, selectionContext);
7✔
313
        BlockRayTraceResultComponent rayTraceResult = selectedShape.rayTrace(pos, selectionContext instanceof EntityCollisionContext ? ((EntityCollisionContext) selectionContext).getEntity() : null);
11!
314
        if (rayTraceResult != null) {
2!
315
            return rayTraceResult.getComponent().getShape(state, world, pos, selectionContext);
×
316
        }
317

318
        String cableState = selectedShape.getStateId();
3✔
319

320
        // Cache the operations below, as they are too expensive to execute each render tick
321
        try {
322
            return CACHE_COLLISION_SHAPES.get(cableState, () -> {
8✔
323
            // Combine all VoxelShapes using IBooleanFunction.OR,
324
            // because for some reason our VoxelShapeComponents aggregator does not handle collisions properly.
325
            // This can probably be fixed, but I spent too much time on this already, and the current solution works just fine.
326
            Iterator<VoxelShape> it = selectedShape.iterator();
3✔
327
            if (!it.hasNext()) {
3✔
328
                return Shapes.empty();
2✔
329
            }
330
            VoxelShape shape = it.next();
4✔
331
            while (it.hasNext()) {
3✔
332
                shape = Shapes.join(shape, it.next(), BooleanOp.OR);
8✔
333
            }
334
            return shape.optimize();
3✔
335
            });
336
        } catch (Exception e) {
×
337
            return Helpers.sneakyThrow(e);
×
338
        }
339
    }
340

341
    @Override
342
    public VoxelShape getCollisionShape(BlockState blockState, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
343
        return (disableCollisionBox || GeneralConfig.disableCableCollision) ? Shapes.empty() : super.getCollisionShape(blockState, world, pos, selectionContext);
12!
344
    }
345

346
    @Override
347
    public boolean hasDynamicShape() {
348
        return BlockCableConfig.dynamicShape;
2✔
349
    }
350

351
    @Override
352
    public RenderShape getRenderShape(BlockState blockState) {
353
        return RenderShape.MODEL;
×
354
    }
355

356
    @Override
357
    protected VoxelShape getBlockSupportShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
358
        return this.getShape(pState, pLevel, pPos, new CollisionContextBlockSupport());
9✔
359
    }
360

361
    @Override
362
    public boolean shouldDisplayFluidOverlay(BlockState state, BlockAndTintGetter world, BlockPos pos, FluidState fluidState) {
363
        return world instanceof ILevelExtension levelExtension && CableHelpers.getFacade(levelExtension, pos, state).isPresent();
×
364
    }
365

366
    /* --------------- Start IDynamicRedstone --------------- */
367

368
    @SuppressWarnings("deprecation")
369
    @Override
370
    public boolean isSignalSource(BlockState blockState) {
371
        return true;
×
372
    }
373

374
    @Override
375
    public boolean canConnectRedstone(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {
376
        if (world instanceof ILevelExtension levelExtension) {
6!
377
            if (side == null) {
2!
378
                for (Direction dummySide : Direction.values()) {
×
379
                    IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, dummySide, Capabilities.DynamicRedstone.BLOCK).orElse(null);
×
380
                    if (dynamicRedstone != null && (dynamicRedstone.getRedstoneLevel() >= 0 || dynamicRedstone.isAllowRedstoneInput())) {
×
381
                        return true;
×
382
                    }
383
                }
384
                return false;
×
385
            }
386
            IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);
12✔
387
            return dynamicRedstone != null && (dynamicRedstone.getRedstoneLevel() >= 0 || dynamicRedstone.isAllowRedstoneInput());
12!
388
        }
389
        return false;
×
390
    }
391

392
    @SuppressWarnings("deprecation")
393
    @Override
394
    public int getDirectSignal(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {
395
        if (world instanceof ILevelExtension levelExtension) {
6!
396
            IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);
12✔
397
            return dynamicRedstone != null && dynamicRedstone.isDirect() ? dynamicRedstone.getRedstoneLevel() : 0;
7!
398
        }
399
        return 0;
×
400
    }
401

402
    @SuppressWarnings("deprecation")
403
    @Override
404
    public int getSignal(BlockState blockState, BlockGetter world, BlockPos pos, Direction side) {
405
        if (world instanceof ILevelExtension levelExtension) {
6!
406
            IDynamicRedstone dynamicRedstone = IModHelpersNeoForge.get().getCapabilityHelpers().getCapability(levelExtension, pos, side.getOpposite(), Capabilities.DynamicRedstone.BLOCK).orElse(null);
12✔
407
            return dynamicRedstone != null ? dynamicRedstone.getRedstoneLevel() : 0;
6!
408
        }
409
        return 0;
×
410
    }
411

412
    /* --------------- Start IDynamicLight --------------- */
413

414
    @Override
415
    public int getLightEmission(BlockState blockState, BlockGetter world, BlockPos pos) {
416
        int light = 0;
2✔
417
        if (world instanceof ILevelExtension levelExtension) {
6✔
418
            for (Direction side : Direction.values()) {
16✔
419
                IDynamicLight dynamicLight = levelExtension.getCapability(Capabilities.DynamicLight.BLOCK, pos, blockState, null, side);
9✔
420
                if (dynamicLight != null) {
2!
421
                    light = Math.max(light, dynamicLight.getLightLevel());
×
422
                }
423
            }
424
        }
425
        return light;
2✔
426
    }
427

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