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

CyclopsMC / IntegratedDynamics / 20210191346

14 Dec 2025 03:32PM UTC coverage: 19.514% (-33.5%) from 53.061%
20210191346

push

github

rubensworks
Remove deprecations

663 of 8728 branches covered (7.6%)

Branch coverage included in aggregate %.

6786 of 29445 relevant lines covered (23.05%)

1.09 hits per line

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

8.95
/src/main/java/org/cyclops/integrateddynamics/core/block/VoxelShapeComponents.java
1
package org.cyclops.integrateddynamics.core.block;
2

3
import com.google.common.collect.Lists;
4
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
5
import it.unimi.dsi.fastutil.doubles.DoubleList;
6
import net.minecraft.core.AxisCycle;
7
import net.minecraft.core.BlockPos;
8
import net.minecraft.core.Direction;
9
import net.minecraft.world.InteractionHand;
10
import net.minecraft.world.InteractionResult;
11
import net.minecraft.world.entity.Entity;
12
import net.minecraft.world.entity.LivingEntity;
13
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
14
import net.minecraft.world.entity.ai.attributes.Attributes;
15
import net.minecraft.world.entity.player.Player;
16
import net.minecraft.world.item.ItemStack;
17
import net.minecraft.world.level.BlockGetter;
18
import net.minecraft.world.level.Level;
19
import net.minecraft.world.level.block.state.BlockState;
20
import net.minecraft.world.phys.AABB;
21
import net.minecraft.world.phys.BlockHitResult;
22
import net.minecraft.world.phys.Vec3;
23
import net.minecraft.world.phys.shapes.CollisionContext;
24
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
25
import net.minecraft.world.phys.shapes.Shapes;
26
import net.minecraft.world.phys.shapes.VoxelShape;
27
import org.apache.commons.lang3.tuple.Pair;
28
import org.cyclops.integrateddynamics.block.shapes.CollisionContextBlockSupport;
29

30
import javax.annotation.Nullable;
31
import java.util.Collection;
32
import java.util.Iterator;
33
import java.util.List;
34
import java.util.stream.Collectors;
35

36
/**
37
 * A {@link VoxelShape} that contains one or more {@link VoxelShapeComponents.IComponent}.
38
 *
39
 * These components are used to handle ray tracing on seperate components.
40
 * Consequently, ray trace results on this object may be safely cast to {@link BlockRayTraceResultComponent}
41
 * so that the targeted component may be retrieved.
42
 *
43
 * @author rubensworks
44
 */
45
public class VoxelShapeComponents extends VoxelShape implements Iterable<VoxelShape> {
46

47
    private final Collection<Pair<VoxelShape, IComponent>> entries;
48
    private final String stateId;
49

50
    protected VoxelShapeComponents(Collection<Pair<VoxelShape, IComponent>> entries, String stateId) {
51
        super(createInnerPart(entries));
4✔
52
        this.entries = entries;
3✔
53
        this.stateId = stateId;
3✔
54
    }
1✔
55

56
    protected static DiscreteVoxelShape createInnerPart(Collection<Pair<VoxelShape, IComponent>> entries) {
57
        return new Part(entries.stream()
6✔
58
                .map(pair -> pair.getLeft().shape)
1✔
59
                .collect(Collectors.toList()));
4✔
60
    }
61

62
    public static VoxelShapeComponents create(BlockState blockState, BlockGetter world, BlockPos blockPos,
63
                                              CollisionContext selectionContext, List<IComponent> components) {
64
        List<Pair<VoxelShape, IComponent>> entries = Lists.newArrayList();
2✔
65
        for (VoxelShapeComponents.IComponent component : components) {
6!
66
            VoxelShape shape = component.getShape(blockState, world, blockPos, selectionContext);
×
67
            entries.add(Pair.of(shape, component));
×
68
        }
×
69

70
        StringBuilder stateIdBuilder = new StringBuilder();
4✔
71
        for (IComponent component : components) {
6!
72
            stateIdBuilder.append(component.getStateId(blockState, world, blockPos));
×
73
            stateIdBuilder.append(";");
×
74
        }
×
75
        stateIdBuilder.append(selectionContext instanceof CollisionContextBlockSupport);
5✔
76

77
        return new VoxelShapeComponents(entries, stateIdBuilder.toString());
7✔
78
    }
79

80
    public String getStateId() {
81
        return this.stateId;
3✔
82
    }
83

84
    @Override
85
    public Iterator<VoxelShape> iterator() {
86
        return entries.stream().map(Pair::getLeft).iterator();
7✔
87
    }
88

89
    @Override
90
    public double min(Direction.Axis axis) {
91
        boolean first = true;
×
92
        double startMin = 0;
×
93
        for (VoxelShape shape : this) {
×
94
            double start = shape.min(axis);
×
95
            if (first || start < startMin) {
×
96
                startMin = start;
×
97
                first = false;
×
98
            }
99
        }
×
100
        return startMin;
×
101
    }
102

103
    @Override
104
    public double max(Direction.Axis axis) {
105
        boolean first = true;
×
106
        double endMax = 0;
×
107
        for (VoxelShape shape : this) {
×
108
            double end = shape.max(axis);
×
109
            if (first || end > endMax) {
×
110
                endMax = end;
×
111
                first = false;
×
112
            }
113
        }
×
114
        return endMax;
×
115
    }
116

117
    @Override
118
    public DoubleList getCoords(Direction.Axis axis) {
119
        DoubleArrayList values = new DoubleArrayList();
×
120
        for (VoxelShape shape : this) {
×
121
            values.addAll(shape.getCoords(axis));
×
122
        }
×
123
        return values;
×
124
    }
125

126
    @Override
127
    public boolean isEmpty() {
128
        for (VoxelShape shape : this) {
×
129
            if (!shape.isEmpty()) {
×
130
                return false;
×
131
            }
132
        }
×
133
        return true;
×
134
    }
135

136
    @Override
137
    public VoxelShape move(double x, double y, double z) {
138
        List<Pair<VoxelShape, IComponent>> entries = Lists.newArrayList();
×
139
        for (Pair<VoxelShape, IComponent> entry : this.entries) {
×
140
            entries.add(Pair.of(entry.getLeft().move(x, y, z), entry.getRight()));
×
141
        }
×
142
        return new VoxelShapeComponents(entries, this.stateId);
×
143
    }
144

145
    @Override
146
    public void forAllEdges(Shapes.DoubleLineConsumer consumer) {
147
        for (VoxelShape shape : this) {
×
148
            shape.forAllEdges(consumer);
×
149
        }
×
150
    }
×
151

152
    @Override
153
    public void forAllBoxes(Shapes.DoubleLineConsumer consumer) {
154
        for (VoxelShape shape : this) {
×
155
            shape.forAllBoxes(consumer);
×
156
        }
×
157
    }
×
158

159
    @Override
160
    public double max(Direction.Axis axis, double a, double b) {
161
        boolean first = true;
×
162
        double valueMax = 0D;
×
163
        for (VoxelShape shape : this) {
×
164
            double value = shape.max(axis, a, b);
×
165
            if (first || value > valueMax) {
×
166
                valueMax = value;
×
167
                first = false;
×
168
            }
169
        }
×
170
        return valueMax;
×
171
    }
172

173
    @Nullable
174
    @Override
175
    public BlockRayTraceResultComponent clip(Vec3 startVec, Vec3 endVec, BlockPos pos) {
176
        // Find component with shape that is closest to the startVec
177
        double distanceMin = Double.POSITIVE_INFINITY;
×
178
        VoxelShapeComponents.IComponent componentMin = null;
×
179
        BlockHitResult resultMin = null;
×
180

181
        for (Pair<VoxelShape, IComponent> entry : entries) {
×
182
            VoxelShape shape = entry.getLeft();
×
183
            BlockHitResult result = shape.clip(startVec, endVec, pos);
×
184
            if (result != null) {
×
185
                double distance = result.getLocation().distanceToSqr(startVec);
×
186
                if (distance < distanceMin) {
×
187
                    // If the previous match was a part and the current one is a facade,
188
                    // check if the direction of that part matches with the matched face of the facade,
189
                    // and if so, don't match the facade,
190
                    // because we want the user to be able to select parts within facades.
191
                    if (resultMin == null || !(entry.getRight().isRaytraceLastForFace() && componentMin.getRaytraceDirection() == result.getDirection())) {
×
192
                        distanceMin = distance;
×
193
                        componentMin = entry.getRight();
×
194
                        resultMin = result;
×
195
                    }
196
                }
197
            }
198
        }
×
199

200
        // Store the component in the ray trace result when we found one.
201
        if (resultMin != null) {
×
202
            return new BlockRayTraceResultComponent(resultMin, componentMin);
×
203
        }
204

205
        return null;
×
206
    }
207

208
    /**
209
     * Do a ray trace for the current look direction of the player.
210
     * @param pos The block position to perform a ray trace for.
211
     * @param entity The entity.
212
     * @return A holder object with information on the ray tracing.
213
     */
214
    @Nullable
215
    public BlockRayTraceResultComponent rayTrace(BlockPos pos, @Nullable Entity entity) {
216
        if(entity == null) {
2!
217
            return null;
2✔
218
        }
219
        AttributeInstance reachDistanceAttribute = entity instanceof LivingEntity ? ((LivingEntity) entity).getAttribute(Attributes.BLOCK_INTERACTION_RANGE) : null;
×
220
        double reachDistance = reachDistanceAttribute == null ? 5 : reachDistanceAttribute.getValue();
×
221

222
        double eyeHeight = entity.level().isClientSide() ? entity.getEyeHeight() : entity.getEyeHeight(); // Client removed :  - player.getDefaultEyeHeight()
×
223
        Vec3 lookVec = entity.getLookAngle();
×
224
        Vec3 origin = new Vec3(entity.getX(), entity.getY() + eyeHeight, entity.getZ());
×
225
        Vec3 direction = origin.add(lookVec.x * reachDistance, lookVec.y * reachDistance, lookVec.z * reachDistance);
×
226

227
        return clip(origin, direction, pos);
×
228
    }
229

230
    @Override
231
    public double collideX(AxisCycle rotation, AABB axisAlignedBB, double range) {
232
        boolean first = true;
×
233
        double valueBest = 0D;
×
234
        for (VoxelShape shape : this) {
×
235
            double value = shape.collideX(rotation, axisAlignedBB, range);
×
236
            if (range > 0) {
×
237
                if (first || value < valueBest) {
×
238
                    valueBest = value;
×
239
                    first = false;
×
240
                }
241
            } else {
242
                if (first || value > valueBest) {
×
243
                    valueBest = value;
×
244
                    first = false;
×
245
                }
246
            }
247
        }
×
248
        return valueBest;
×
249
    }
250

251
    public static class Part extends DiscreteVoxelShape implements Iterable<DiscreteVoxelShape> {
252

253
        private final Collection<DiscreteVoxelShape> entries;
254

255
        public Part(Collection<DiscreteVoxelShape> entries) {
256
            super(0, 0, 0);
5✔
257
            this.entries = entries;
3✔
258
        }
1✔
259

260
        @Override
261
        public Iterator<DiscreteVoxelShape> iterator() {
262
            return entries.iterator();
×
263
        }
264

265
        @Override
266
        public boolean isFullWide(int x, int y, int z) {
267
            for (DiscreteVoxelShape part : this) {
×
268
                if (part.isFullWide(x, y, z)) {
×
269
                    return true;
×
270
                }
271
            }
×
272
            return false;
×
273
        }
274

275
        @Override
276
        public boolean isFull(int x, int y, int z) {
277
            for (DiscreteVoxelShape part : this) {
×
278
                if (part.isFull(x, y, z)) {
×
279
                    return true;
×
280
                }
281
            }
×
282
            return false;
×
283
        }
284

285
        @Override
286
        public void fill(int x, int y, int z) {
287
            for (DiscreteVoxelShape part : this) {
×
288
                part.fill(x, y, z);
×
289
            }
×
290
        }
×
291

292
        @Override
293
        public int firstFull(Direction.Axis axis) {
294
            boolean first = true;
×
295
            int startMin = 0;
×
296
            for (DiscreteVoxelShape part : this) {
×
297
                int start = part.firstFull(axis);
×
298
                if (first || start < startMin) {
×
299
                    startMin = start;
×
300
                    first = false;
×
301
                }
302
            }
×
303
            return startMin;
×
304
        }
305

306
        @Override
307
        public int lastFull(Direction.Axis axis) {
308
            boolean first = true;
×
309
            int endMax = 0;
×
310
            for (DiscreteVoxelShape part : this) {
×
311
                int end = part.lastFull(axis);
×
312
                if (first || end > endMax) {
×
313
                    endMax = end;
×
314
                    first = false;
×
315
                }
316
            }
×
317
            return endMax;
×
318
        }
319

320
        @Override
321
        public int getSize(Direction.Axis axis) {
322
            boolean first = true;
×
323
            int sizeMax = 0;
×
324
            for (DiscreteVoxelShape part : this) {
×
325
                int size = part.getSize(axis);
×
326
                if (first || size > sizeMax) {
×
327
                    sizeMax = size;
×
328
                    first = false;
×
329
                }
330
            }
×
331
            return sizeMax;
×
332
        }
333

334
        @Override
335
        public void forAllBoxes(IntLineConsumer consumer, boolean p_197831_2_) {
336
            for (DiscreteVoxelShape part : this) {
×
337
                part.forAllBoxes(consumer, p_197831_2_);
×
338
            }
×
339
        }
×
340
    }
341

342
    public static interface IComponent {
343

344
        /**
345
         * @param blockState The block state.
346
         * @param world The world.
347
         * @param blockPos The position.
348
         * @return Unique identifier for the component's state.
349
         */
350
        public String getStateId(BlockState blockState, BlockGetter world, BlockPos blockPos);
351

352
        /**
353
         * Get the shape of this component.
354
         * @param blockState The block state.
355
         * @param world The world.
356
         * @param blockPos The position.
357
         * @param selectionContext The selection context.
358
         * @return The shape.
359
         */
360
        public VoxelShape getShape(BlockState blockState, BlockGetter world, BlockPos blockPos, CollisionContext selectionContext);
361

362
        /**
363
         * Get the pick block item.
364
         * @param world The world
365
         * @param pos The position
366
         * @return The item.
367
         */
368
        public ItemStack getCloneItemStack(Level world, BlockPos pos);
369

370
        /**
371
         * Destroy this component
372
         * @param world The world
373
         * @param pos The position
374
         * @param player The player destroying the component.
375
         * @param saveState If the component state should be saved in the dropped item.
376
         * @return If the complete block was destroyed
377
         */
378
        public boolean destroy(Level world, BlockPos pos, Player player, boolean saveState);
379

380
        /**
381
         * When this component has been activated.
382
         * @param state The block state.
383
         * @param world The world.
384
         * @param blockPos The position.
385
         * @param player The player.
386
         * @param hand The hand.
387
         * @param hit The ray trace result.
388
         * @return Action result.
389
         */
390
        public InteractionResult onBlockActivated(BlockState state, Level world, BlockPos blockPos, Player player,
391
                                                 InteractionHand hand, BlockRayTraceResultComponent hit);
392

393
        /**
394
         * @return The direction this component points at.
395
         */
396
        @Nullable
397
        public Direction getRaytraceDirection();
398

399
        /**
400
         * @return If this component should only be raytraced if no other components matched for this face.
401
         */
402
        public boolean isRaytraceLastForFace();
403

404
    }
405

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