Resources

A resource is a folder in the server's resources folder with a package.json. Its scripts define your server's commands, gamemodes, shops and anything else. You do not need to build the server or the mod, and players download a resource's client part when they join.

For a step-by-step start, see Your first resource. The smallest working resource is two files:

{
  "name": "hello",
  "version": "1.0.0",
  "kcds": {
    "serverScripts": ["server.js"]
  }
}
const { Events, Chat } = kcds.core;

Events.on('playerCommand', (player, command, args) => {
  if (command.toLowerCase() !== 'hello') {
    return;
  }
  Chat.sendToPlayer(player, args.length > 0 ? args.join(' ') : 'Hello!', { author: 'Server' });
});

The API is in the global kcds object and needs no import; start from the API overview. Commands arrive without the slash, with arguments split at spaces. Case is kept, so do your own case-insensitive comparison.

TypeScript

You can write in TypeScript if you compile it to CommonJS first and point the manifest at the compiled files. Each release includes the SDK's declarations in KCDS-Types-<version>.zip. See TypeScript for a project setup.

The manifest

KCDS reads the "kcds" section of package.json. A resource is identified by its name, not its folder.

KeyDefaultMeaning
serverScripts[]server scripts (CommonJS), in the order they run
clientScripts[]scripts each player's game runs; see Client resources
sharedScripts[]scripts for both sides, run before that side's own list
files (clientFiles)[]files sent to players but not run, such as pages, pictures and data
loadingScreennoneyour page over joining and loading, sent first; see Web views
exports[]names shared with other resources; see Imports and exports
requires[]resources that must run first: names, or { "name": "x", "optional": true }
errorBehavior"stop"what an uncaught error does: stop, restart (after one second) or continue
priority0recorded, but does not change the start order

File patterns

files takes relative paths with /, and patterns can use *, ?, **, [a-z] and *.{css,json}.

If files is empty, the folders of the client entry scripts are sent. With client.js next to package.json, that is every file except the server entry scripts. Hidden files such as .env and .git, and node_modules, are never sent that way.

A pattern matches hidden files only if it names the dot, but a broad one like ** does include node_modules. Symbolic links are not followed.

NoteKeep keys, settings and server modules out of client folders and broad patterns. The simplest way is a separate client folder; see Project structure.

Size limits

Each file sent to players can be up to 8 MB. A resource over a limit is not sent to players. The server logs <name>: not sent to players: ..., its server scripts still run, and everything else carries on. If a refresh pushes a resource over a limit, players keep the version they have.

What scripts can load

Both sides can require the resource's own .js, .cjs and .json files by relative path. Client scripts can load nothing else.

Server scripts can also load:

  • Node's modules, such as node:fs and node:path
  • packages in the resource's folder, after npm install pg there
  • all of the above through globalThis.require, with relative paths from the resource's folder, which compiled code often uses

process.cwd() is the resource's folder, and a deployment replaces it. Save data under kcds.core.Runtime.serverDir instead, the server's folder containing resources/ and server.json.

Network access from server scripts

Server scripts have fetch and WebSocket for things like a chat bot or a link to your services. When the resource stops, its requests are aborted and its sockets closed. A resource can have up to 64 sockets open.

const socket = new WebSocket('wss://example.com/bot');

socket.addEventListener('open', () => socket.send(JSON.stringify({ hello: 'kcds' })));
socket.addEventListener('message', (event) => console.info(`bot says ${event.data}`));

Starting and stopping

Once a resource's scripts have run, all resources receive resourceStart(name), so compare the name with your own.

KCDS waits for async handlers. Until the handler finishes, the resource is still starting and resources that require it wait. If it rejects or takes over 30 seconds, startup fails, and KCDS removes everything the resource registered.

When a resource stops, all resources first receive resourceStop(name), while the stopping one still works fully. Its dependents stop before it. Async handlers get up to 10 seconds. Clean-up of handlers, timers, exports, messages and anything it spawned always happens.

const { Events } = kcds.core;

Events.on('resourceStart', async (name) => {
  if (name === 'hello') {
    console.info('hello is ready');
  }
});

Events.on('resourceStop', (name) => {
  if (name === 'hello') {
    console.info('saving before hello stops');
  }
});

Complete examples

The server comes with resources\freeroam and resources\dice, which show working server and client scripts.