Skip to content

Commit 90d52f4

Browse files
authored
Significant Projectile Simulator/Trajectories improvements (#5999)
- The projectile simulator is now perfectly accurate (almost, apart from server desyncs and certain edge cases) - Most of the simulator bugs should be fixed - Now predicts piercing arrows (only for held crossbows) and projectiles getting deflected (breeze, shield, world border) - Added a new setting to the Trajectories module to skip rendering the first few ticks of the path for better visibility
1 parent 0c2dc94 commit 90d52f4

File tree

8 files changed

+596
-360
lines changed

8 files changed

+596
-360
lines changed

src/main/java/meteordevelopment/meteorclient/mixin/Vec3dMixin.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,26 @@ public abstract class Vec3dMixin implements IVec3d {
1919
@Shadow @Final @Mutable public double z;
2020

2121
@Override
22-
public void meteor$set(double x, double y, double z) {
22+
public Vec3d meteor$set(double x, double y, double z) {
2323
this.x = x;
2424
this.y = y;
2525
this.z = z;
26+
27+
return (Vec3d) (Object) this;
2628
}
2729

2830
@Override
29-
public void meteor$setXZ(double x, double z) {
31+
public Vec3d meteor$setXZ(double x, double z) {
3032
this.x = x;
3133
this.z = z;
34+
35+
return (Vec3d) (Object) this;
3236
}
3337

3438
@Override
35-
public void meteor$setY(double y) {
39+
public Vec3d meteor$setY(double y) {
3640
this.y = y;
41+
42+
return (Vec3d) (Object) this;
3743
}
3844
}

src/main/java/meteordevelopment/meteorclient/mixininterface/IVec3d.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,27 @@
55

66
package meteordevelopment.meteorclient.mixininterface;
77

8+
import net.minecraft.util.math.Vec3d;
89
import net.minecraft.util.math.Vec3i;
910
import org.joml.Vector3d;
1011

12+
@SuppressWarnings("UnusedReturnValue")
1113
public interface IVec3d {
12-
void meteor$set(double x, double y, double z);
14+
Vec3d meteor$set(double x, double y, double z);
1315

14-
default void meteor$set(Vec3i vec) {
15-
meteor$set(vec.getX(), vec.getY(), vec.getZ());
16+
default Vec3d meteor$set(Vec3i vec) {
17+
return meteor$set(vec.getX(), vec.getY(), vec.getZ());
1618
}
1719

18-
default void meteor$set(Vector3d vec) {
19-
meteor$set(vec.x, vec.y, vec.z);
20+
default Vec3d meteor$set(Vector3d vec) {
21+
return meteor$set(vec.x, vec.y, vec.z);
2022
}
2123

22-
void meteor$setXZ(double x, double z);
24+
default Vec3d meteor$set(Vec3d pos) {
25+
return meteor$set(pos.x, pos.y, pos.z);
26+
}
27+
28+
Vec3d meteor$setXZ(double x, double z);
2329

24-
void meteor$setY(double y);
30+
Vec3d meteor$setY(double y);
2531
}

src/main/java/meteordevelopment/meteorclient/systems/modules/combat/ArrowDodge.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
import meteordevelopment.meteorclient.settings.*;
1010
import meteordevelopment.meteorclient.systems.modules.Categories;
1111
import meteordevelopment.meteorclient.systems.modules.Module;
12-
import meteordevelopment.meteorclient.utils.entity.ProjectileEntitySimulator;
12+
import meteordevelopment.meteorclient.utils.entity.simulator.ProjectileEntitySimulator;
1313
import meteordevelopment.meteorclient.utils.misc.Pool;
1414
import meteordevelopment.orbit.EventHandler;
1515
import net.minecraft.entity.Entity;
1616
import net.minecraft.entity.projectile.ArrowEntity;
1717
import net.minecraft.entity.projectile.ProjectileEntity;
18+
import net.minecraft.entity.projectile.SpectralArrowEntity;
1819
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
1920
import net.minecraft.util.math.BlockPos;
2021
import net.minecraft.util.math.Vec3d;
@@ -54,13 +55,6 @@ public class ArrowDodge extends Module {
5455
.build()
5556
);
5657

57-
private final Setting<Boolean> accurate = sgGeneral.add(new BoolSetting.Builder()
58-
.name("accurate")
59-
.description("Whether or not to calculate more accurate.")
60-
.defaultValue(false)
61-
.build()
62-
);
63-
6458
private final Setting<Boolean> groundCheck = sgGeneral.add(new BoolSetting.Builder()
6559
.name("ground-check")
6660
.description("Tries to prevent you from falling to your death.")
@@ -78,7 +72,7 @@ public class ArrowDodge extends Module {
7872
private final Setting<Boolean> ignoreOwn = sgGeneral.add(new BoolSetting.Builder()
7973
.name("ignore-own")
8074
.description("Ignore your own projectiles.")
81-
.defaultValue(false)
75+
.defaultValue(true)
8276
.build()
8377
);
8478

@@ -116,16 +110,16 @@ private void onTick(TickEvent.Pre event) {
116110

117111
for (Entity e : mc.world.getEntities()) {
118112
if (!(e instanceof ProjectileEntity projectile)) continue;
119-
if (!allProjectiles.get() && !(projectile instanceof ArrowEntity)) continue;
113+
if (!allProjectiles.get() && !(projectile instanceof ArrowEntity || projectile instanceof SpectralArrowEntity)) continue;
120114
if (ignoreOwn.get()) {
121115
Entity owner = projectile.getOwner();
122116
if (owner != null && owner.getUuid().equals(mc.player.getUuid())) continue;
123117
}
124118

125-
if (!simulator.set(projectile, accurate.get())) continue;
119+
if (!simulator.set(projectile)) continue;
126120
for (int i = 0; i < (simulationSteps.get() > 0 ? simulationSteps.get() : Integer.MAX_VALUE); i++) {
127121
points.add(vec3s.get().set(simulator.pos));
128-
if (simulator.tick() != null) break;
122+
if (simulator.tick().shouldStop) break;
129123
}
130124
}
131125

src/main/java/meteordevelopment/meteorclient/systems/modules/render/Trajectories.java

Lines changed: 91 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
import meteordevelopment.meteorclient.systems.modules.Categories;
1212
import meteordevelopment.meteorclient.systems.modules.Module;
1313
import meteordevelopment.meteorclient.utils.Utils;
14-
import meteordevelopment.meteorclient.utils.entity.ProjectileEntitySimulator;
14+
import meteordevelopment.meteorclient.utils.entity.simulator.ProjectileEntitySimulator;
15+
import meteordevelopment.meteorclient.utils.entity.simulator.SimulationStep;
1516
import meteordevelopment.meteorclient.utils.misc.Pool;
1617
import meteordevelopment.meteorclient.utils.render.color.SettingColor;
1718
import meteordevelopment.orbit.EventHandler;
1819
import net.minecraft.enchantment.Enchantments;
1920
import net.minecraft.entity.Entity;
2021
import net.minecraft.entity.player.PlayerEntity;
2122
import net.minecraft.entity.projectile.ProjectileEntity;
23+
import net.minecraft.entity.projectile.TridentEntity;
2224
import net.minecraft.entity.projectile.WitherSkullEntity;
2325
import net.minecraft.item.*;
2426
import net.minecraft.registry.Registries;
@@ -86,6 +88,14 @@ public class Trajectories extends Module {
8688

8789
// Render
8890

91+
private final Setting<Integer> ignoreFirstTicks = sgRender.add(new IntSetting.Builder()
92+
.name("ignore-rendering-first-ticks")
93+
.description("Ignores rendering the first given ticks, to make the rest of the path more visible.")
94+
.defaultValue(3)
95+
.min(0)
96+
.build()
97+
);
98+
8999
private final Setting<ShapeMode> shapeMode = sgRender.add(new EnumSetting.Builder<ShapeMode>()
90100
.name("shape-mode")
91101
.description("How the shapes are rendered.")
@@ -189,22 +199,25 @@ private void calculatePath(PlayerEntity player, float tickDelta) {
189199

190200
// Calculate paths
191201
if (!simulator.set(player, itemStack, 0, accurate.get(), tickDelta)) return;
192-
getEmptyPath().calculate();
202+
Path p = getEmptyPath().calculate();
203+
if (player == mc.player) p.ignoreFirstTicks();
193204

194205
if (itemStack.getItem() instanceof CrossbowItem && Utils.hasEnchantment(itemStack, Enchantments.MULTISHOT)) {
195206
if (!simulator.set(player, itemStack, MULTISHOT_OFFSET, accurate.get(), tickDelta)) return; // left multishot arrow
196-
getEmptyPath().calculate();
207+
p = getEmptyPath().calculate();
208+
if (player == mc.player) p.ignoreFirstTicks();
197209

198210
if (!simulator.set(player, itemStack, -MULTISHOT_OFFSET, accurate.get(), tickDelta)) return; // right multishot arrow
199-
getEmptyPath().calculate();
211+
p = getEmptyPath().calculate();
212+
if (player == mc.player) p.ignoreFirstTicks();
200213
}
201214
}
202215

203216
private void calculateFiredPath(Entity entity, double tickDelta) {
204217
for (Path path : paths) path.clear();
205218

206219
// Calculate paths
207-
if (!simulator.set(entity, accurate.get())) return;
220+
if (!simulator.set(entity)) return;
208221
getEmptyPath().setStart(entity, tickDelta).calculate();
209222
}
210223

@@ -221,7 +234,10 @@ private void onRender(Render3DEvent event) {
221234

222235
if (firedProjectiles.get()) {
223236
for (Entity entity : mc.world.getEntities()) {
224-
if (entity instanceof ProjectileEntity && (!ignoreWitherSkulls.get() || !(entity instanceof WitherSkullEntity))) {
237+
if (entity instanceof ProjectileEntity) {
238+
if (ignoreWitherSkulls.get() && entity instanceof WitherSkullEntity) continue;
239+
if (entity instanceof TridentEntity trident && trident.noClip) continue; // when it's returning via loyalty
240+
225241
calculateFiredPath(entity, tickDelta);
226242
for (Path path : paths) path.render(event);
227243
}
@@ -235,31 +251,33 @@ private class Path {
235251
private boolean hitQuad, hitQuadHorizontal;
236252
private double hitQuadX1, hitQuadY1, hitQuadZ1, hitQuadX2, hitQuadY2, hitQuadZ2;
237253

238-
private Entity collidingEntity;
254+
private final List<Entity> collidingEntities = new ArrayList<>();
239255
public Vector3d lastPoint;
256+
private int start;
240257

241258
public void clear() {
242259
vec3s.freeAll(points);
243260
points.clear();
244261

245262
hitQuad = false;
246-
collidingEntity = null;
263+
collidingEntities.clear();
247264
lastPoint = null;
265+
start = 0;
248266
}
249267

250-
public void calculate() {
268+
public Path calculate() {
251269
addPoint();
252270

253271
for (int i = 0; i < (simulationSteps.get() > 0 ? simulationSteps.get() : Integer.MAX_VALUE); i++) {
254-
HitResult result = simulator.tick();
272+
SimulationStep result = simulator.tick();
255273

256-
if (result != null) {
257-
processHitResult(result);
258-
break;
259-
}
274+
processHitResults(result);
275+
if (result.shouldStop) break;
260276

261277
addPoint();
262278
}
279+
280+
return this;
263281
}
264282

265283
public Path setStart(Entity entity, double tickDelta) {
@@ -276,58 +294,75 @@ private void addPoint() {
276294
points.add(vec3s.get().set(simulator.pos));
277295
}
278296

279-
private void processHitResult(HitResult result) {
280-
if (result.getType() == HitResult.Type.BLOCK) {
281-
BlockHitResult r = (BlockHitResult) result;
282-
283-
hitQuad = true;
284-
hitQuadX1 = r.getPos().x;
285-
hitQuadY1 = r.getPos().y;
286-
hitQuadZ1 = r.getPos().z;
287-
hitQuadX2 = r.getPos().x;
288-
hitQuadY2 = r.getPos().y;
289-
hitQuadZ2 = r.getPos().z;
290-
291-
if (r.getSide() == Direction.UP || r.getSide() == Direction.DOWN) {
292-
hitQuadHorizontal = true;
293-
hitQuadX1 -= 0.25;
294-
hitQuadZ1 -= 0.25;
295-
hitQuadX2 += 0.25;
296-
hitQuadZ2 += 0.25;
297-
}
298-
else if (r.getSide() == Direction.NORTH || r.getSide() == Direction.SOUTH) {
299-
hitQuadHorizontal = false;
300-
hitQuadX1 -= 0.25;
301-
hitQuadY1 -= 0.25;
302-
hitQuadX2 += 0.25;
303-
hitQuadY2 += 0.25;
304-
}
305-
else {
306-
hitQuadHorizontal = false;
307-
hitQuadZ1 -= 0.25;
308-
hitQuadY1 -= 0.25;
309-
hitQuadZ2 += 0.25;
310-
hitQuadY2 += 0.25;
297+
private void processHitResults(SimulationStep step) {
298+
for (int i = 0; i < step.hitResults.length; i++) {
299+
HitResult result = step.hitResults[i];
300+
if (result.getType() == HitResult.Type.BLOCK) {
301+
BlockHitResult r = (BlockHitResult) result;
302+
303+
hitQuad = true;
304+
hitQuadX1 = r.getPos().x;
305+
hitQuadY1 = r.getPos().y;
306+
hitQuadZ1 = r.getPos().z;
307+
hitQuadX2 = r.getPos().x;
308+
hitQuadY2 = r.getPos().y;
309+
hitQuadZ2 = r.getPos().z;
310+
311+
if (r.getSide() == Direction.UP || r.getSide() == Direction.DOWN) {
312+
hitQuadHorizontal = true;
313+
hitQuadX1 -= 0.25;
314+
hitQuadZ1 -= 0.25;
315+
hitQuadX2 += 0.25;
316+
hitQuadZ2 += 0.25;
317+
}
318+
else if (r.getSide() == Direction.NORTH || r.getSide() == Direction.SOUTH) {
319+
hitQuadHorizontal = false;
320+
hitQuadX1 -= 0.25;
321+
hitQuadY1 -= 0.25;
322+
hitQuadX2 += 0.25;
323+
hitQuadY2 += 0.25;
324+
}
325+
else {
326+
hitQuadHorizontal = false;
327+
hitQuadZ1 -= 0.25;
328+
hitQuadY1 -= 0.25;
329+
hitQuadZ2 += 0.25;
330+
hitQuadY2 += 0.25;
331+
}
332+
333+
points.add(Utils.set(vec3s.get(), result.getPos()));
311334
}
335+
else if (result.getType() == HitResult.Type.ENTITY) {
336+
Entity entity = ((EntityHitResult) result).getEntity();
337+
collidingEntities.add(entity);
312338

313-
points.add(Utils.set(vec3s.get(), result.getPos()));
339+
if (step.shouldStop && i == step.hitResults.length - 1) {
340+
points.add(Utils.set(vec3s.get(), result.getPos()));
341+
}
342+
}
314343
}
315-
else if (result.getType() == HitResult.Type.ENTITY) {
316-
collidingEntity = ((EntityHitResult) result).getEntity();
344+
}
317345

318-
points.add(Utils.set(vec3s.get(), result.getPos()).add(0, collidingEntity.getHeight() / 2, 0));
319-
}
346+
public void ignoreFirstTicks() {
347+
start = points.size() <= ignoreFirstTicks.get() ? 0 : ignoreFirstTicks.get();
320348
}
321349

322350
public void render(Render3DEvent event) {
323351
// Render path
324-
for (Vector3d point : points) {
352+
for (int i = start; i < points.size(); i++) {
353+
Vector3d point = points.get(i);
354+
325355
if (lastPoint != null) {
326356
event.renderer.line(lastPoint.x, lastPoint.y, lastPoint.z, point.x, point.y, point.z, lineColor.get());
327-
if (renderPositionBox.get())
328-
event.renderer.box(point.x - positionBoxSize.get(), point.y - positionBoxSize.get(), point.z - positionBoxSize.get(),
329-
point.x + positionBoxSize.get(), point.y + positionBoxSize.get(), point.z + positionBoxSize.get(), positionSideColor.get(), positionLineColor.get(), shapeMode.get(), 0);
357+
if (renderPositionBox.get()) {
358+
event.renderer.box(
359+
point.x - positionBoxSize.get(), point.y - positionBoxSize.get(), point.z - positionBoxSize.get(),
360+
point.x + positionBoxSize.get(), point.y + positionBoxSize.get(), point.z + positionBoxSize.get(),
361+
positionSideColor.get(), positionLineColor.get(), shapeMode.get(), 0
362+
);
363+
}
330364
}
365+
331366
lastPoint = point;
332367
}
333368

@@ -338,7 +373,7 @@ public void render(Render3DEvent event) {
338373
}
339374

340375
// Render entity
341-
if (collidingEntity != null) {
376+
for (Entity collidingEntity : collidingEntities) {
342377
double x = (collidingEntity.getX() - collidingEntity.lastX) * event.tickDelta;
343378
double y = (collidingEntity.getY() - collidingEntity.lastY) * event.tickDelta;
344379
double z = (collidingEntity.getZ() - collidingEntity.lastZ) * event.tickDelta;

0 commit comments

Comments
 (0)