JS Scripting Quickstart¶
CrespoGuard ZoneMod includes a built-in JavaScript engine inside the Zone Server.
You write plain .js files, drop them into a folder, and the server executes them — no
compiler, no external runtime, no restarts required.
| Concept | Detail |
|---|---|
| Engine | JavaScript (ES2020, single-threaded, sandboxed) |
| Script folder | CrespoGuard/scripts/ |
| Plugin folder | CrespoGuard/plugins/ (protected .cgp bytecode) |
| Entry point | on() — register a callback for any game event |
| Hot-reload | /js reload in-game or via the Dashboard |
Your First Script¶
Create a file called welcome.js:
on('player.login', function(player) {
player.sendMessage('Welcome to the server, ' + player.name + '!');
});
Deploying
- Save the file to
CrespoGuard/scripts/welcome.js. - In-game, type
/js reload— or press Reload Scripts in the Dashboard. - Log in with a character and you should see the welcome message.
No server restart needed.
How Events Work¶
Every script revolves around events. You call on() with an event name and a
callback function. The engine does the rest.
Events come in three flavours:
Info only — you can read data but cannot change or cancel the action.
Return false to cancel the action before it happens.
Event name matters
The chat event is player.chat — not player.chatCircle.
Check the Event Reference for exact names.
File Structure¶
CrespoGuard/
├── plugins/ # Protected .cgp bytecode (don't edit)
├── scripts/ # Your custom .js scripts
│ ├── welcome.js
│ ├── custom_rates.js
│ ├── _disabled/ # Scripts here are skipped on load
│ └── store.dat # Persistent data (auto-managed)
Load priority
If a .cgp plugin and a .js script share the same base name, the .cgp file
takes priority and the .js file is ignored.
Available APIs¶
A quick map of what you can call from inside a script. Each heading links to its full reference page.
Player Object¶
The player argument passed to most event callbacks.
player.name // character name (string)
player.level // current level (number)
player.race // 0 = Bellato, 1 = Cora, 2 = Accretia
player.sendMessage(text) // private system message
player.addBuff(code, index, dur, lvl) // apply a buff
player.removeBuffByIndex(index) // remove buff by effect index
Buff channels
addBuff accepts an optional 5th argument channel: 0 = skill (default),
1 = force. Bellato/Cora force buffs need channel 1; Accretia skill buffs
use channel 0.
GameServer¶
Server-wide operations available as a global object.
GameServer.broadcast(text) // message to all online players
GameServer.getPlayerByName(name) // returns a Player or null
GameServer.playerCount // number of players online (property)
Store (Persistent Data)¶
Key-value storage that survives server restarts. Data is saved to store.dat
automatically.
Store.set('topKiller', player.name);
var killer = Store.get('topKiller'); // returns string or undefined
Store.delete('topKiller');
Timers¶
Standard timer functions, scoped to the script lifetime.
setTimeout(function() {
GameServer.broadcast('Server event starting!');
}, 60000); // 60 seconds
var id = setInterval(function() {
// runs every 5 minutes
}, 300000);
clearInterval(id);
Event Reference¶
There are 309+ events covering combat, trade, crafting, movement, and more. See the full Event Reference for the complete list.
Practical Example¶
A small "double rates" script that combines several APIs:
// Double EXP + drop rate on weekends
function isWeekend() {
var day = new Date().getDay();
return day === 0 || day === 6; // Sunday or Saturday
}
on('player.gainExp', function(player, amount) {
if (isWeekend()) return amount * 2;
});
on('player.login', function(player) {
if (isWeekend()) {
player.sendMessage('[EVENT] Double EXP weekend is active!');
}
});
Debugging Tips¶
| Technique | Usage |
|---|---|
console.log(value) |
Prints to the server console window |
scriptLog(text) |
Writes to the dedicated script log file |
/js reload |
Reload all scripts without restarting |
/js list |
Show loaded scripts and their status |
Disable without deleting
Move a script into the _disabled/ subfolder to skip it on load. Move it
back when you are ready to re-enable.
Common Pitfalls¶
Array.isArray vs JS_IsArray
Use Array.isArray(val) to check arrays. The internal JS_IsArray(val) takes
one argument only — passing two arguments will cause unexpected behavior.
Don't block the main thread
Scripts run on the server's main thread. Avoid infinite loops or heavy
computation — use setTimeout to break work into chunks if needed.
Persistent data
Store is the only way to persist data across reloads and restarts. Local
variables reset every time scripts are reloaded.
Next Steps¶
- API Reference — Player, GameServer, Monster, Guild, Store, Timers
- Event Reference — all 309+ hookable events