Skip to content

Commit c89ad0e

Browse files
Merge pull request #833 from Swofty-Developments/chore/minestom-26.2
feat(mob): vanilla mob AI on Minestom 26.2
2 parents 65a2eb9 + 9f5e7a0 commit c89ad0e

32 files changed

Lines changed: 1184 additions & 116 deletions

File tree

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ junit = "4.13.2"
2020
junit-jupiter = "6.1.0"
2121
kotlin-stdlib = "2.3.20"
2222
lombok = "1.18.46"
23-
minestom = "2026.06.05-26.1.2"
23+
minestom = "2026.07.22-26.2"
2424
mongodb = "5.6.4"
2525
netty-bom = "4.2.9.Final"
2626
polar = "1.16.0"

type.bedwarsgame/src/main/java/net/swofty/type/bedwarsgame/game/v2/listener/PlayerTeamAssignationListener.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import net.swofty.type.generic.event.EventNodes;
1313
import net.swofty.type.generic.event.phase.PhasedEvent;
1414
import net.swofty.type.generic.event.HypixelEventClass;
15+
import net.swofty.type.generic.utility.TeamColorUtility;
1516

1617
public class PlayerTeamAssignationListener implements HypixelEventClass {
1718

@@ -24,7 +25,7 @@ public void onPlayerTeamAssign(PlayerAssignedTeamEvent<BedWarsTeam> event) {
2425
NamedTextColor color = NamedTextColor.nearestTo(textColor); // possible performance hit?
2526
player.setTeam(new TeamBuilder(event.team().getName(), MinecraftServer.getTeamManager())
2627
.prefix(Component.text(event.team().firstLetter(), textColor, TextDecoration.BOLD).appendSpace())
27-
.teamColor(color)
28+
.teamColor(TeamColorUtility.fromNamedColor(color))
2829
.build());
2930
player.getTeam().sendUpdatePacket();
3031
}

type.generic/src/main/java/net/swofty/type/generic/HypixelGenericLoader.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import net.minestom.server.instance.InstanceContainer;
2525
import net.minestom.server.instance.InstanceManager;
2626
import net.minestom.server.instance.block.Block;
27-
import net.minestom.server.monitoring.BenchmarkManager;
2827
import net.minestom.server.monitoring.TickMonitor;
2928
import net.minestom.server.registry.RegistryKey;
3029
import net.minestom.server.timer.TaskSchedule;
@@ -69,7 +68,6 @@
6968
import java.io.IOException;
7069
import java.lang.reflect.InvocationTargetException;
7170
import java.nio.file.Files;
72-
import java.time.Duration;
7371
import java.util.ArrayList;
7472
import java.util.Collection;
7573
import java.util.List;
@@ -180,14 +178,12 @@ public int getBiomeId(@NotNull String name) {
180178
if (!isSkyBlockType && !(loader.getType() == ServerType.BEDWARS_GAME)) {
181179
MinecraftServer.getGlobalEventHandler().addListener(ServerTickMonitorEvent.class, event ->
182180
LAST_TICK.set(event.getTickMonitor()));
183-
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
184-
benchmarkManager.enable(Duration.ofDays(3));
185181
MinecraftServer.getSchedulerManager().buildTask(() -> {
186182
Collection<HypixelPlayer> players = getLoadedPlayers();
187183
if (players.isEmpty())
188184
return;
189185

190-
long ramUsage = benchmarkManager.getUsedMemory();
186+
long ramUsage = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
191187
ramUsage /= (long) 1e6; // bytes to MB
192188
TickMonitor tickMonitor = LAST_TICK.get();
193189
double TPS = 1000 / tickMonitor.getTickTime();

type.generic/src/main/java/net/swofty/type/generic/data/HypixelDataHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import net.minestom.server.entity.Player;
88
import net.minestom.server.scoreboard.Team;
99
import net.minestom.server.scoreboard.TeamBuilder;
10+
import net.swofty.type.generic.utility.TeamColorUtility;
1011
import net.swofty.commons.ServerType;
1112
import net.swofty.commons.StringUtility;
1213
import net.swofty.type.generic.HypixelConst;
@@ -190,7 +191,7 @@ public enum Data {
190191
String teamName = StringUtility.limitStringLength(rank.getPriorityCharacter() + player.getUsername(), 15);
191192
Team team = new TeamBuilder("H" + teamName, MinecraftServer.getTeamManager())
192193
.prefix(((HypixelPlayer) player).getRankPrefix())
193-
.teamColor(rank.getTextColor())
194+
.teamColor(TeamColorUtility.fromNamedColor(rank.getTextColor()))
194195
.build();
195196
player.setTeam(team);
196197
player.getTeam().sendUpdatePacket();
@@ -204,7 +205,7 @@ public enum Data {
204205
String teamName = StringUtility.limitStringLength(rank.getPriorityCharacter() + player.getUsername(), 15);
205206
player.setTeam(new TeamBuilder("H" + teamName, MinecraftServer.getTeamManager())
206207
.prefix(((HypixelPlayer) player).getRankPrefix())
207-
.teamColor(rank.getTextColor())
208+
.teamColor(TeamColorUtility.fromNamedColor(rank.getTextColor()))
208209
.build());
209210
player.getTeam().sendUpdatePacket();
210211
}),
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package net.swofty.type.generic.entity.ai.vanilla;
2+
3+
/** Port of {@code net.minecraft.world.level.pathfinder.PathComputationType}. */
4+
public enum PathComputationType {
5+
LAND,
6+
WATER,
7+
AIR;
8+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package net.swofty.type.generic.entity.ai.vanilla;
2+
3+
/** Port of {@code net.minecraft.world.level.pathfinder.PathType} (27 node classifications). */
4+
public enum PathType {
5+
BLOCKED(-1.0F),
6+
OPEN(0.0F),
7+
WALKABLE(0.0F),
8+
WALKABLE_DOOR(0.0F),
9+
TRAPDOOR(0.0F),
10+
POWDER_SNOW(-1.0F),
11+
ON_TOP_OF_POWDER_SNOW(0.0F),
12+
FENCE(-1.0F),
13+
LAVA(-1.0F),
14+
WATER(8.0F),
15+
WATER_BORDER(8.0F),
16+
RAIL(0.0F),
17+
UNPASSABLE_RAIL(-1.0F),
18+
FIRE_IN_NEIGHBOR(8.0F),
19+
FIRE(16.0F),
20+
DAMAGING_IN_NEIGHBOR(8.0F),
21+
DAMAGING(-1.0F),
22+
DOOR_OPEN(0.0F),
23+
DOOR_WOOD_CLOSED(-1.0F),
24+
DOOR_IRON_CLOSED(-1.0F),
25+
BREACH(4.0F),
26+
LEAVES(-1.0F),
27+
STICKY_HONEY(8.0F),
28+
COCOA(0.0F),
29+
DAMAGE_CAUTIOUS(0.0F),
30+
ON_TOP_OF_TRAPDOOR(0.0F),
31+
BIG_MOBS_CLOSE_TO_DANGER(4.0F);
32+
33+
private final float malus;
34+
35+
PathType(final float defaultCost) {
36+
this.malus = defaultCost;
37+
}
38+
39+
public float getMalus() {
40+
return this.malus;
41+
}
42+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package net.swofty.type.generic.entity.ai.vanilla;
2+
3+
import net.minestom.server.collision.BoundingBox;
4+
import net.minestom.server.collision.CollisionUtils;
5+
import net.minestom.server.collision.PhysicsResult;
6+
import net.minestom.server.coordinate.Point;
7+
import net.minestom.server.coordinate.Pos;
8+
import net.minestom.server.coordinate.Vec;
9+
import net.minestom.server.entity.Entity;
10+
import net.minestom.server.entity.LivingEntity;
11+
import net.minestom.server.entity.attribute.Attribute;
12+
import net.minestom.server.entity.pathfinding.followers.NodeFollower;
13+
import net.minestom.server.instance.Instance;
14+
import net.minestom.server.utils.position.PositionUtils;
15+
import org.jetbrains.annotations.Nullable;
16+
17+
/**
18+
* A {@link NodeFollower} that moves an entity along a path with vanilla ground movement. It mirrors
19+
* Minestom's own {@code GroundNodeFollower} (horizontal {@code Vec} toward the target,
20+
* {@link CollisionUtils#handlePhysics} then {@code refreshPosition} with yaw/pitch from the look
21+
* point) but adds vanilla auto-step: when the horizontal move is blocked and the entity is on the
22+
* ground it retries the move raised by up to {@link #STEP_HEIGHT} and settles back down, so it
23+
* climbs 0.5-1.0 block ledges smoothly instead of only being able to jump them.
24+
*/
25+
public class VanillaGroundNodeFollower implements NodeFollower {
26+
/** Vanilla maximum auto-step height for a ground mob. */
27+
private static final double STEP_HEIGHT = 0.6;
28+
/** Horizontal "arrived" radius. */
29+
private static final double AT_POINT_RADIUS = 0.2;
30+
/** Vanilla jump impulse: ~0.42 blocks/tick, expressed in Minestom's blocks/second velocity. */
31+
private static final double JUMP_VELOCITY = 0.42 * 20.0;
32+
33+
private final Entity entity;
34+
35+
public VanillaGroundNodeFollower(Entity entity) {
36+
this.entity = entity;
37+
}
38+
39+
@Override
40+
public void moveTowards(Point target, double speed, Point lookAt) {
41+
Pos position = this.entity.getPosition();
42+
double dx = target.x() - position.x();
43+
double dy = target.y() - position.y();
44+
double dz = target.z() - position.z();
45+
double dxLook = lookAt.x() - position.x();
46+
double dyLook = lookAt.y() - position.y();
47+
double dzLook = lookAt.z() - position.z();
48+
49+
// Slow down as the entity reaches its destination (mirrors GroundNodeFollower).
50+
double distSquared = dx * dx + dy * dy + dz * dz;
51+
if (speed > distSquared) {
52+
speed = distSquared;
53+
}
54+
55+
double radians = Math.atan2(dz, dx);
56+
double speedX = Math.cos(radians) * speed;
57+
double speedZ = Math.sin(radians) * speed;
58+
float yaw = PositionUtils.getLookYaw(dxLook, dzLook);
59+
float pitch = PositionUtils.getLookPitch(dxLook, dyLook, dzLook);
60+
61+
Vec move = new Vec(speedX, 0.0, speedZ);
62+
PhysicsResult ground = CollisionUtils.handlePhysics(this.entity, move);
63+
Pos newPosition = ground.newPosition();
64+
65+
// Vanilla auto-step: if we hit a wall while on the ground, try climbing a low ledge.
66+
Instance instance = this.entity.getInstance();
67+
if (instance != null && this.entity.isOnGround() && (ground.collisionX() || ground.collisionZ())) {
68+
BoundingBox boundingBox = this.entity.getBoundingBox();
69+
Pos raised = position.withY(position.y() + STEP_HEIGHT);
70+
PhysicsResult stepped = CollisionUtils.handlePhysics(instance, boundingBox, raised, move, null, false);
71+
if (horizontalProgress(stepped.newPosition(), position) > horizontalProgress(ground.newPosition(), position) + 1.0E-4) {
72+
// Cleared the ledge raised; settle back onto its top so we don't float.
73+
PhysicsResult settle = CollisionUtils.handlePhysics(instance, boundingBox, stepped.newPosition(), new Vec(0.0, -STEP_HEIGHT, 0.0), null, false);
74+
newPosition = settle.newPosition();
75+
}
76+
}
77+
78+
this.entity.refreshPosition(newPosition.withView(yaw, pitch));
79+
}
80+
81+
private static double horizontalProgress(Point moved, Point origin) {
82+
double dx = moved.x() - origin.x();
83+
double dz = moved.z() - origin.z();
84+
return dx * dx + dz * dz;
85+
}
86+
87+
@Override
88+
public void jump(@Nullable Point from, @Nullable Point to) {
89+
if (this.entity.isOnGround()) {
90+
this.entity.setVelocity(new Vec(0.0, JUMP_VELOCITY, 0.0));
91+
}
92+
}
93+
94+
@Override
95+
public boolean isAtPoint(Point point) {
96+
Pos position = this.entity.getPosition();
97+
double dx = position.x() - point.x();
98+
double dz = position.z() - point.z();
99+
return dx * dx + dz * dz <= AT_POINT_RADIUS * AT_POINT_RADIUS;
100+
}
101+
102+
@Override
103+
public double movementSpeed() {
104+
return this.entity instanceof LivingEntity living ? living.getAttribute(Attribute.MOVEMENT_SPEED).getValue() : 0.1;
105+
}
106+
}

0 commit comments

Comments
 (0)