Mineflayer sending flying packets out of spec - Incompatible with servers running Grim AC
- The FAQ doesn't contain a resolution to my issue
Versions
- mineflayer: 4.33.0
- server: vanilla/spigot/paper 1.21.4
- node: v24.11.0
Description of problem
Mineflayer bot is sending "flying" packets out of spec, and is tipping off GrimAC. Some servers running GrimAC will automatically kick bot, even if the bot is just standing still.
Let's say I spin up a bare-bones, basic Mineflayer bot as so:
import mineflayer from 'mineflayer';
const bot = mineflayer.createBot({
host: 'MyAnarchyServer.xyz',
username: 'mybot',
auth: 'microsoft'
})
// Log errors and kick reasons:
bot.on('kicked', console.log)
bot.on('error', console.log)As soon as the bot joins my server, Grim will immediately start complaining that the bot is failing checks:
[16:34:11 INFO]: Grim » BotUsername failed TickTimer (x83) type=flying, packets=83
[16:34:11 INFO]: Grim » BotUsername failed TickTimer (x84) type=flying, packets=84
[16:34:11 INFO]: Grim » BotUsername failed TickTimer (x85) type=flying, packets=85
[16:34:11 INFO]: Grim » BotUsername failed TickTimer (x86) type=flying, packets=86
[16:34:11 INFO]: Grim » BotUsername failed TickTimer (x87) type=flying, packets=87On some Anarchy servers, the bot will get kicked after about a minute of uptime, even if it hasn't moved.
I did some digging into Grim's code base, and found that this error message was generated by a TickTimer class:
public static boolean isFlying(PacketTypeCommon type) {
return type == PacketType.Play.Client.PLAYER_FLYING
|| type == PacketType.Play.Client.PLAYER_POSITION
|| type == PacketType.Play.Client.PLAYER_ROTATION
|| type == PacketType.Play.Client.PLAYER_POSITION_AND_ROTATION;
}
...
@CheckData(name = "TickTimer", setback = 1)
public class TickTimer extends Check implements PacketCheck {
private boolean receivedTickEnd = true;
private int flyingPackets = 0;
public TickTimer(GrimPlayer player) {
super(player);
}
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (!player.supportsEndTick()) return;
if (isFlying(event.getPacketType()) && !player.packetStateData.lastPacketWasTeleport) {
if (!receivedTickEnd && flagAndAlertWithSetback("type=flying, packets=" + flyingPackets)) {
handleViolation();
}
receivedTickEnd = false;
flyingPackets++;
} else if (event.getPacketType() == PacketType.Play.Client.CLIENT_TICK_END) {
receivedTickEnd = true;
if (flyingPackets > 1 && flagAndAlertWithSetback("type=end, packets=" + flyingPackets)) {
handleViolation();
}
flyingPackets = 0;
}
}
private void handleViolation() {
// Although we don't cancel the packet, this should be counted as an invalid packet.
player.onPacketCancel();
}
}
Following through the code, it appears that Grim is flagging "flying" packets that are received if the last packed was not a teleport packet.
I wound up running a packet logger on my bare-bones Mineflayer bot, and logging all outgoing packets to a file.
const originalWrite = client.write.bind(client)
client.write = (name, data) => {
logLine(`OUT ${name} ${safeStringify(data)}`)
return originalWrite(name, data)
}Here is an excerpt from that experiment:
[2026-01-01T22:39:38.085Z] OUT pong {"id":-105}
[2026-01-01T22:39:38.085Z] OUT pong {"id":-106}
[2026-01-01T22:39:38.122Z] OUT pong {"id":-107}
[2026-01-01T22:39:38.134Z] OUT look {"x":2.8315,"y":-63,"z":-12.306750000000001,"yaw":174.6000213623047,"pitch":4.800000190734863,"onGround":false,"time":4318.878149,"flags":{"onGround":false}}
[2026-01-01T22:39:38.135Z] OUT pong {"id":-108}
[2026-01-01T22:39:38.135Z] OUT teleport_confirm {"teleportId":-1827424533}
[2026-01-01T22:39:38.135Z] OUT position_look {"x":2.8315,"y":-63,"z":-12.306750000000001,"yaw":174.6000213623047,"pitch":4.800000190734869,"onGround":false,"time":4318.878149,"flags":{"onGround":false}}Source: PrismarineJS/mineflayer