npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

classicflayer

v1.1.1

Published

Mineflayer-style bot & server library for Minecraft Classic / ClassiCube

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 classicflayer

Module 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 server

Chat

bot.chat('Hello world!')
// Auto-splits messages longer than 64 characters

Movement & 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 movement

Looking

// 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 successful

Block 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 loaded

Search & 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 jumping

Methods

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 |