classicflayer
v1.1.1
Published
Mineflayer-style bot & server library for Minecraft Classic / ClassiCube
Maintainers
Readme
🧱 classicflayer
Mineflayer-style bot & server library for Minecraft Classic / ClassiCube.
Version:1.1.0· Node.js>=18· Dependency:classic-node-protocol
What is classicflayer?
A Node.js library that lets you:
- Create bots that connect to Minecraft Classic / ClassiCube servers
- Host your own Classic/ClassiCube servers
- Handle physics, movement, blocks, chat, and players
The API is inspired by Mineflayer (the Java Edition bot library) — if you already know it, you'll feel right at home.
Installation
npm install classicflayerModule Exports
const cf = require('classicflayer');
cf.createBot(options) // Factory: create a bot
cf.createServer(options) // Factory: create a server
cf.Bot // Bot class
cf.Server // Server class
cf.PlayerSession // PlayerSession class (player connected to your server)
cf.World // World class (block map)
cf.Physics // Physics class (physics engine)
cf.Navigator // Navigator class (basic pathfinding)
cf.BLOCKS // Block constants { STONE: 1, GRASS: 2, ... }
cf.BLOCK_NAMES // Block names { 1: 'Stone', 2: 'Grass', ... }
cf.VERSION // '1.0.0'🤖 BOT API
cf.createBot(options) → Bot
Creates a bot and connects it automatically. This is the main entry point.
const bot = cf.createBot({
host: 'play.classicube.net', // Server to connect to
port: 25565, // Port (default: 25565)
username: 'MyBot', // Bot username
verificationKey: '-', // ClassiCube verification key (default: '-')
physics: true, // Enable physics (default: true)
connectTimeout: 10000, // Timeout in ms (default: 10000)
});Bot Properties
| Property | Type | Description |
|----------|------|-------------|
| bot.username | string | Bot's username |
| bot.entity | object | { id, username, x, y, z, yaw, pitch } — bot state |
| bot.position | object | { x, y, z } — current position (getter) |
| bot.players | Map | Online players: username → { id, x, y, z, yaw, pitch } |
| bot.heldBlock | number | Currently held block ID (default: STONE) |
| bot.world | World | Loaded world instance |
| bot.physics | Physics | Physics engine |
| bot.navigate | Navigator | Pathfinding helper |
Bot Methods
Connection
await bot.connect() // Connect manually (createBot already calls this)
bot.disconnect() // Disconnect from the serverChat
bot.chat('Hello world!')
// Auto-splits messages longer than 64 charactersMovement & Controls
// Same as Mineflayer: toggle movement keys on/off
bot.setControlState('forward', true) // Move forward
bot.setControlState('back', true) // Move backward
bot.setControlState('left', true) // Strafe left
bot.setControlState('right', true) // Strafe right
bot.setControlState('jump', true) // Jump / swim up
bot.setControlState('sneak', true) // Sneak / climb down ladder
bot.clearControlStates() // Stop all movementLooking
// Look at specific angles (in degrees)
bot.look(yawDeg, pitchDeg, callback?)
// Look toward a world position
bot.lookAt(x, y, z)Block Interaction
// Get block info
const block = bot.blockAt(x, y, z)
// → { x, y, z, type: 1, name: 'Stone' }
// Break a block
bot.dig(x, y, z)
// Place a block
bot.placeBlock(x, y, z) // Uses heldBlock
bot.placeBlock(x, y, z, BLOCKS.STONE) // Specific block type
// Change held block
bot.equip(cf.BLOCKS.GRASS)Finding Blocks
// Nearest block of a given type
const nearest = bot.findBlock(cf.BLOCKS.STONE, 32) // radius = 32
// → { x, y, z } or null
// Distance to a point
const dist = bot.distanceTo(x, y, z)Navigation (bot.navigate)
// Walk to a position (returns a Promise)
await bot.navigate.goto(10, 32, 10)
// With options
await bot.navigate.goto(10, 32, 10, { tolerance: 1.0 })
// Stop navigation
bot.navigate.stop()The Navigator uses real physics + collision. It auto-jumps 1-block obstacles.
Bot Events
bot.on('spawn', () => {
// Bot spawned in the world — ready to act
})
bot.on('connect', ({ serverName, motd }) => {
// Server identification received
})
bot.on('levelLoaded', ({ xSize, ySize, zSize }) => {
// World fully loaded
})
bot.on('levelProgress', (percent) => {
// World loading progress (0–100)
})
bot.on('chat', (username, message, rawLine) => {
// A player sent a chat message
if (message === 'hi') bot.chat('Hey ' + username)
})
bot.on('message', (rawMessage) => {
// Server message that doesn't match "user: text" format
})
bot.on('playerJoined', ({ id, username, x, y, z }) => {
// A player joined the server
})
bot.on('playerLeft', ({ id, username }) => {
// A player left the server
})
bot.on('blockUpdate', ({ x, y, z, blockId }) => {
// A block changed in the world
})
bot.on('diggingCompleted', ({ x, y, z }) => {
// Bot finished breaking a block
})
bot.on('blockPlaced', ({ x, y, z, type }) => {
// Bot placed a block
})
bot.on('jump', () => { /* Bot jumped */ })
bot.on('land', (pos) => { /* Bot landed */ })
bot.on('enterWater', (pos) => { /* Entered water */ })
bot.on('exitWater', (pos) => { /* Exited water */ })
bot.on('forcedMove', (pos) => { /* Server forced a position update */ })
bot.on('kicked', (reason) => {
// Bot was kicked
})
bot.on('end', () => {
// Connection closed
})
bot.on('error', (err) => {
// Connection error
})🖥️ SERVER API
cf.createServer(options) → Server
Creates a server and starts listening for connections.
const server = cf.createServer({
port: 25565, // Port (default: 25565)
name: 'My Server', // Server name
motd: 'Powered by classicflayer', // Message of the day
maxPlayers: 20, // Player cap (default: 20)
world: { // World settings
xSize: 128, // Width (default: 128)
ySize: 64, // Height (default: 64)
zSize: 128, // Length (default: 128)
blocks: myCustomBlocks, // Custom block buffer (optional)
},
});Server Properties
| Property | Type | Description |
|----------|------|-------------|
| server.name | string | Server name |
| server.motd | string | Message of the day |
| server.maxPlayers | number | Player cap |
| server.players | Map | Connected players: id → PlayerSession |
| server.world | World | World instance |
Server Methods
// Send a message to everyone
server.broadcast('Welcome everyone!')
server.broadcast('Message', excludePlayerId) // Exclude one player
// Set a block in the world AND broadcast to all players
server.setBlock(x, y, z, cf.BLOCKS.STONE)
// Start listening (createServer already calls this)
server.listen(25565)
// Shut down the server
server.close()Server Events
server.on('login', (player) => {
// A player connected (PlayerSession)
player.sendMessage('Welcome, ' + player.username + '!')
server.broadcast(player.username + ' joined the game')
})
server.on('playerLeft', (player) => {
// A player disconnected
})
server.on('chat', (player, message) => {
// A player sent a chat message
// If you don't listen to this event, the server auto-broadcasts it
server.broadcast('<' + player.username + '> ' + message)
})
server.on('blockSet', (player, x, y, z, mode, blockType) => {
// A player tried to place/break a block
// mode: 0 = break, 1 = place
console.log(player.username, 'changed block at', x, y, z)
})
server.on('listening', (port) => {
console.log('Server listening on port', port)
})
server.on('close', () => {
console.log('Server closed')
})
server.on('error', (err, player?) => {
// Error (player is optional)
})
server.on('message', (text) => {
// Mirror of every broadcast (useful for logging)
})👤 PlayerSession API
Every player connected to your server is a PlayerSession.
Properties
| Property | Type | Description |
|----------|------|-------------|
| player.id | number | Unique session ID |
| player.username | string | Player's username |
| player.x/y/z | number | Current position |
| player.yaw/pitch | number | Current rotation |
| player.position | object | { x, y, z } (getter) |
| player.isConnected | boolean | Whether still connected |
| player.data | object | Free-form object to store your app data |
Methods
player.sendMessage('Hey!') // Send a private message
player.kick('Get out of here') // Kick the player
player.teleport(x, y, z, yaw?, pitch?) // Teleport
player.distanceTo(x, y, z) // Distance to a point🌍 World API
Access the block map. Available on both bot.world and server.world.
Block Access
world.getBlock(x, y, z) // → Block ID (0 = air)
world.getBlockName(x, y, z) // → 'Stone', 'Grass', etc.
world.setBlock(x, y, z, blockId) // → true if successfulBlock Property Queries
world.isSolid(x, y, z) // Has collision AABB
world.isPassable(x, y, z) // Can walk through (air, plants, liquids)
world.isLiquid(x, y, z) // Water or lava
world.isLava(x, y, z) // Lava specifically
world.isClimbable(x, y, z) // Ladder / vine
world.isIce(x, y, z) // Ice (slippery)
world.isGravityBlock(x, y, z) // Sand / gravel (falls)
world.getBlockHeight(x, y, z) // Collision height (0.5 for slabs, 1.0 normal)
world.inBounds(x, y, z) // Is inside the map
world.loaded // Whether the world is loadedSearch & Utilities
// Find blocks by type near a point
world.findBlocks(blockId, { x, y, z }, radius, limit?)
// → [{ x, y, z }, ...] sorted by distance
// Ray-cast
world.raycast(origin, direction, maxDist?)
// → { x, y, z, face: 'top'|'bottom'|'north'|..., blockId } | null
// 6 face-adjacent neighbors
world.neighbours(x, y, z)
// → [{ x, y, z, blockId }, ...]
// Is it safe to stand here?
world.isSafeStand(x, y, z)
// Simulate sand/gravel falling
world.simulateFallingBlock(x, y, z) // → final Y, or -1 if fell out of world
world.triggerGravity(x, y, z) // → true if a fall was triggered⚙️ Physics API
The bot's physics engine. Available at bot.physics.
State
physics.x, physics.y, physics.z // Position (feet)
physics.vx, physics.vy, physics.vz // Velocity (blocks/tick)
physics.onGround // Touching ground
physics.inWater // Submerged in water
physics.inLava // Submerged in lava
physics.onLadder // Touching a climbable block
physics.isJumping // Currently jumpingMethods
physics.start() // Start physics loop (20 ticks/sec)
physics.stop() // Stop physics loop
physics.setPosition(x, y, z) // Force position
physics.jump() // Jump (or swim up)
physics.climbUp() // Climb up a ladder
physics.climbDown() // Climb down a ladder
physics.walkTo(tx, tz, tolerance) // Move toward X,Z → true when arrived📦 Block Constants
const { BLOCKS, BLOCK_NAMES } = require('classicflayer');
BLOCKS.AIR // 0
BLOCKS.STONE // 1
BLOCKS.GRASS // 2
BLOCKS.DIRT // 3
BLOCKS.COBBLESTONE // 4
BLOCKS.WOOD // 5
BLOCKS.SAND // 12
BLOCKS.GRAVEL // 13
BLOCKS.WATER // 8
BLOCKS.LAVA // 10
// ... and many more (up to ID 65)
BLOCK_NAMES[1] // → 'Stone'
BLOCK_NAMES[12] // → 'Sand'🗂️ Full Examples
Bot that greets players and mines blocks
const cf = require('classicflayer');
const bot = cf.createBot({
host: 'play.classicube.net',
port: 25565,
username: 'Greetbot',
});
bot.on('spawn', async () => {
bot.chat('Hey everyone!');
// Find the nearest stone block
const stone = bot.findBlock(cf.BLOCKS.STONE, 20);
if (stone) {
await bot.navigate.goto(stone.x, stone.y, stone.z);
bot.dig(stone.x, stone.y, stone.z);
bot.chat('Got some stone!');
}
});
bot.on('chat', (username, msg) => {
if (msg === '!pos') {
const { x, y, z } = bot.position;
bot.chat(`I'm at ${x.toFixed(1)}, ${y.toFixed(1)}, ${z.toFixed(1)}`);
}
});
bot.on('error', console.error);Server with basic anti-griefing
const cf = require('classicflayer');
const server = cf.createServer({
port: 25565,
name: 'My Server',
motd: 'Welcome!',
maxPlayers: 10,
});
const admins = new Set(['MyUsername']);
server.on('login', (player) => {
player.sendMessage('&aWelcome, ' + player.username + '!');
server.broadcast('&e' + player.username + ' joined');
});
server.on('chat', (player, msg) => {
if (msg === '!players') {
player.sendMessage('Online: ' + server.players.size);
return;
}
server.broadcast('<' + player.username + '> ' + msg);
});
server.on('blockSet', (player, x, y, z, mode, blockType) => {
// Only admins can break bedrock
if (mode === 0 && server.world.getBlock(x, y, z) === cf.BLOCKS.BEDROCK) {
if (!admins.has(player.username)) {
player.sendMessage("&cYou can't break bedrock!");
return false; // block the action
}
}
});File Structure
classicflayer/
├── index.js ← Entry point, re-exports everything
├── package.json
└── lib/
├── bot.js ← Bot + Navigator + createBot()
├── server.js ← Server + PlayerSession + createServer()
├── world.js ← World + block constants & flags
└── physics.js ← Physics engine (20 ticks/sec)Physics Reference Constants
| Parameter | Value | |-----------|-------| | Gravity | 0.08 b/tick² | | Jump force | 0.42 b/tick | | Walk speed | 0.215 b/tick | | Air drag | 0.91/tick | | Ground drag | 0.546/tick | | Water drag | 0.80/tick | | Lava drag | 0.50/tick | | Tick rate | 50ms (20 ticks/sec) | | Player AABB | 0.6 wide × 1.8 tall |
