How server and client plugins talk to each other.

Server ↔ Client Communication

Server and client plugins communicate through named channels — a simple message-passing system.

Server → Client

Send to one player

// In a server plugin:
ctx.sendToClient(entityId, "quest-tracker", {
  questId: "clear_mine",
  progress: 7,
  target: 10,
});

Broadcast to all players

ctx.broadcastToClients("world-event", {
  type: "boss-spawn",
  bossName: "Dragon King",
  startsIn: 60,
});

Receive on client

// In a client plugin:
ctx.on("quest-tracker", function(data) {
  ctx.updateWidget(questWidgetId, {
    html: "Quest: " + data.questId +
          " — " + data.progress + "/" + data.target,
  });
});

ctx.on("world-event", function(data) { if (data.type === "boss-spawn") { ctx.addLocalChatMessage( "[EVENT] " + data.bossName + " spawns in " + data.startsIn + "s!" ); } });

Client → Server

Send from client

ctx.send("auction-bid", {
  itemId: "rare-sword-001",
  amount: 500,
});

Receive on server

ctx.onClientMessage("auction-bid", (entityId, data) => {
  ctx.log.info(Player ${entityId} bid ${data.amount} on ${data.itemId});
  ctx.sendToClient(entityId, "auction-result", {
    success: true,
    message: "Bid placed!",
  });
});

Best Practices

  • Use descriptive channel names"auction:bid" not "data"
  • Always validate on the server — never trust client data
  • Keep payloads small — only send what's needed
  • Handle missing data — check for undefined/null in handlers