Write Minecraft Plugins in JavaScript - No Java Required!
A powerful Minecraft plugin that allows you to create server plugins using JavaScript instead of Java.
Features โข Installation โข Quick Start โข Documentation โข Examples โข API Reference
๐ก New to MC-JS? Start with the Quick Start guide or check out our complete documentation.
- ๐ Full JavaScript Support - Write plugins in modern JavaScript (ES6+) using Rhino Engine
- โก Hot Reload - Reload plugins without restarting the server (
/jsreload) - ๐ง Complete API Access - Access to virtually all Bukkit/Spigot/Paper API functions
- ๐ฎ Event System - Register listeners for any Minecraft event with priority support
- ๐ฌ Command System - Create custom commands with full tab completion support
- โฐ Task Scheduling - Synchronous and asynchronous task scheduling
- ๐ฆ Inventory Management - Create and manage custom GUIs with click handlers
- ๐๏ธ Database Support - Built-in SQLite database operations (INSERT, UPDATE, DELETE, SELECT)
- ๐ HTTP Requests - Make HTTP GET/POST requests asynchronously
- ๐ Encryption - MD5, SHA256, Base64 encoding/decoding
- ๐ File I/O - YAML, JSON, and text file support
- ๐จ BossBar Support - Create and manage boss bars with progress tracking
- โฑ๏ธ Cooldown System - Built-in cooldown management per player
- โ๏ธ Config System - Per-plugin configuration files (YAML)
- ๐ World Management - Control weather, time, world border, explosions
- ๐ฏ Entity Management - Spawn, control, and customize entities
- ๐ Player Management - Ban, kick, teleport, health, food, gamemode
- ๐จ Particle Effects - Spawn particles with string or enum support
- ๐ Sound System - Play sounds at locations or for players
- ๐ Scoreboard System - Create and manage scoreboards, teams, objectives
- ๐ Item Manipulation - Create, modify, and manage items with custom names and lore
- ๐ Permission System - Check and manage player permissions
- ๐จ Color Support - Minecraft color codes and formatting utilities
- ๐ HTTP Integration - Make external API calls and web requests
- ๐ Economy Integration - Vault economy support for server economies
- Minecraft Server: Paper/Spigot 1.20+ (recommended: Paper)
- Java: Version 21 or higher
- Minecraft Version: 1.20+
- Download the latest release from the Releases page
- Place the JAR file in your server's
plugins/folder - Start or restart your server
- Create JS plugins in
plugins/MC-JS/js-plugins/
The plugin will automatically create the js-plugins directory and copy an example plugin on first run.
- Minecraft server running Paper/Spigot 1.20+
- MC-JS plugin installed in
plugins/folder - Basic JavaScript knowledge
- Navigate to
plugins/MC-JS/js-plugins/directory - Create a new file with a
.jsextension (e.g.,myplugin.js) - Add the following code:
// Plugin metadata
var pluginInfo = {
name: "My First Plugin",
version: "1.0.0",
author: "YourName",
description: "My awesome plugin!"
};
function onEnable() {
logger.info("My plugin is enabled!");
// Register a command
api.registerCommand("hello", "Say hello", "/hello", function(sender, args) {
api.sendMessage(sender, "&aHello from JavaScript!");
return true;
});
// Register an event
api.registerEvent("player.PlayerJoinEvent", function(event) {
var player = event.getPlayer();
api.sendMessage(player, "&6Welcome to the server!");
});
}
function onDisable() {
logger.info("My plugin is disabled!");
}
// Export functions
this.onEnable = onEnable;
this.onDisable = onDisable;
this.pluginInfo = pluginInfo;- Save the file - The plugin will auto-load on server start, or use
/jsreloadto reload - Test your command - Type
/helloin-game or in console - Check console - Look for "My plugin is enabled!" message
๐ก Tip: Use
/jslistto see all loaded JavaScript plugins and/jsreload <plugin>to reload a specific plugin.
๐ Full Documentation: Visit our complete documentation website for detailed API reference, examples, and guides.
These objects are automatically available in all JavaScript plugins:
These objects are available in all JavaScript plugins:
| Object | Description | Usage |
|---|---|---|
api |
Complete JS API wrapper - main interface for all operations | api.registerCommand(...) |
server |
Minecraft server instance | server.getOnlinePlayers() |
plugin |
Main plugin instance | plugin.getName() |
logger |
Plugin logger | logger.info("Message") |
scheduler |
Server scheduler | scheduler.runTask(...) |
Bukkit |
Direct Bukkit API access | Bukkit.getServer() |
// Simple command
api.registerCommand("command", function(sender, args) {
api.sendMessage(sender, "Command executed!");
return true;
});
// Command with description and usage
api.registerCommand("command", "Description", "/command [args]", function(sender, args) {
// Your code here
return true;
});
// Command with tab completion
api.registerCommand("command", "Description", "/command [args]",
function(sender, args) {
// Command executor
return true;
},
function(sender, args) {
// Tab completer - return array of strings
return ["option1", "option2", "option3"];
}
);// Register event by string name
api.registerEvent("player.PlayerJoinEvent", function(event) {
var player = event.getPlayer();
api.sendMessage(player, "Welcome!");
});
// Register with priority
api.registerEvent("block.BlockBreakEvent", function(event) {
// HIGH priority - runs before NORMAL
}, "HIGH");
// Available priorities: LOWEST, LOW, NORMAL, HIGH, HIGHEST, MONITORAvailable Event Packages:
player.*- Player events (PlayerJoinEvent, PlayerQuitEvent, etc.)block.*- Block events (BlockBreakEvent, BlockPlaceEvent, etc.)entity.*- Entity eventsinventory.*- Inventory eventsserver.*- Server events
// Run task after delay (in ticks, 20 ticks = 1 second)
api.runTaskLater(100, function() {
logger.info("This runs after 5 seconds");
});
// Run repeating task
var task = api.runTaskTimer(0, 1200, function() {
logger.info("This runs every minute");
});
// Cancel task
api.cancelTask(task);
// Run async task (for non-blocking operations)
api.runTaskAsync(function() {
// This runs in a separate thread
var data = api.httpGet("https://api.example.com/data");
logger.info("Fetched: " + data);
});// Get players
var player = api.getPlayer("PlayerName");
var allPlayers = api.getOnlinePlayers();
// Send messages
api.sendMessage(player, "&aHello!");
api.broadcast("&6Server announcement!");
api.sendTitle(player, "&aTitle", "&eSubtitle");
api.sendActionBar(player, "&bAction bar message");
// Player properties
api.setHealth(player, 20.0);
api.setFoodLevel(player, 20);
api.setSaturation(player, 20.0); // Set saturation level
api.setGameMode(player, api.getMaterial("CREATIVE")); // Use GameMode enum
api.teleport(player, location);
// Clear player inventory (including armor)
api.clearInventory(player);// Method 1: Create inventory manually
var inv = api.createInventory(null, 27, "&6My GUI");
// Method 2: Use GUI Builder (recommended - easier and more powerful)
var gui = api.inventory.createGUI("&6My GUI", 3); // 3 rows = 27 slots
gui.setItem(10, item1)
.setItem(12, item2)
.setItem(14, item3)
.onClick(function(event) {
event.setCancelled(true);
var slot = event.getSlot();
if (slot === 10) {
api.sendMessage(event.getWhoClicked(), "&aYou clicked item 1!");
}
})
.onClose(function(event) {
api.sendMessage(event.getPlayer(), "&7GUI closed!");
});
gui.open(player);
// Create item
var item = api.createItemStack(api.getMaterial("DIAMOND"), 1);
item = api.setItemDisplayName(item, "&b&lSpecial Diamond");
item = api.setItemLore(item, [
"&7Line 1",
"&7Line 2"
]);
// Set item in inventory
api.setInventoryItem(inv, 0, item);
// Register inventory click handler
api.registerInventoryClick(inv, function(event) {
event.setCancelled(true);
api.sendMessage(event.getWhoClicked(), "You clicked slot " + event.getSlot());
});// Create table
api.createTable("mydb", "players", {
"id": "INTEGER PRIMARY KEY",
"name": "TEXT",
"level": "INTEGER"
});
// Insert data
api.insertData("mydb", "players", {
"name": "PlayerName",
"level": 10
});
// Update data
api.updateData("mydb", "players",
{ "level": 20 },
"name = 'PlayerName'"
);
// Query data
var results = api.querySQL("mydb", "SELECT * FROM players WHERE level > 5");
for (var i = 0; i < results.length; i++) {
logger.info("Player: " + results[i].name + ", Level: " + results[i].level);
}
// Delete data
api.deleteData("mydb", "players", "level < 5");
// Count rows
var count = api.countRows("mydb", "players");// YAML files
var data = { "key": "value", "number": 42 };
api.saveYamlFile("config", data);
var loaded = api.loadYamlFile("config");
// JSON files
api.saveJsonFile("data", JSON.stringify({ "key": "value" }));
var json = api.loadJsonFile("data");
var obj = JSON.parse(json);
// Text files
api.saveTextFile("log", "Some text content");
var text = api.loadTextFile("log");// Get plugin config (creates if doesn't exist)
var config = api.getPluginConfig("myplugin");
// Get config value with default
var setting = api.getPluginConfigValue("myplugin", "setting", "default");
// Set config value
api.setPluginConfigValue("myplugin", "setting", "new value");
// Save entire config
api.savePluginConfig("myplugin", { "setting1": "value1", "setting2": "value2" });// Check cooldown
if (!api.hasCooldown(player.getName(), "command")) {
// Execute command
api.setCooldown(player.getName(), "command", 5000); // 5 seconds
} else {
var remaining = api.getCooldownRemaining(player.getName(), "command");
api.sendMessage(player, "&cCooldown: " + (remaining / 1000) + " seconds");
}
// Remove cooldown
api.removeCooldown(player.getName(), "command");// Create boss bar
var bossBar = api.createBossBar("&cBoss Fight!", "RED", "SOLID");
bossBar.addPlayer(player);
bossBar.setProgress(0.5); // 50%
// Update boss bar
bossBar.setTitle("&6New Title");
bossBar.setProgress(0.75);
// Remove boss bar
bossBar.removePlayer(player);
bossBar.removeAll();// Spawn particles (string or enum)
api.spawnParticle(location, "FIREWORK", 10, 0.5, 0.5, 0.5, 0.1);
api.spawnParticle(location, Particle.FLAME, 5, 0, 0, 0);
// Play sounds
api.playSound(location, "ENTITY_PLAYER_LEVELUP", 1.0, 1.0);
api.playSound(player, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.5, 1.5);// String utilities
var colored = api.colorize("&aHello &bWorld");
var stripped = api.stripColor(colored);
var formatted = api.format("Hello %s!", "World");
// Math utilities
var rounded = api.round(3.14159, 2); // 3.14
var clamped = api.clamp(150, 0, 100); // 100
// Encoding
var encoded = api.base64Encode("Hello");
var decoded = api.base64Decode(encoded);
var md5 = api.md5("text");
var sha256 = api.sha256("text");
// Date/Time
var now = api.getCurrentTimeMillis();
var formatted = api.formatDate(now);
var parsed = api.parseDate("2024-01-01 12:00:00");var pluginInfo = {
name: "Welcome Plugin",
version: "1.0.0",
author: "YourName"
};
function onEnable() {
api.registerEvent("player.PlayerJoinEvent", function(event) {
var player = event.getPlayer();
// Welcome message
api.sendMessage(player, "&6Welcome to the server, &b" + player.getName() + "&6!");
// Title
api.sendTitle(player, "&aWelcome!", "&eEnjoy your stay!", 10, 70, 20);
// Sound
api.playSound(player, "ENTITY_PLAYER_LEVELUP", 1.0, 1.0);
// Give welcome kit
api.runTaskLater(20, function() {
var bread = api.createItemStack(api.getMaterial("BREAD"), 5);
api.giveItem(player, bread);
});
});
}
this.onEnable = onEnable;
this.pluginInfo = pluginInfo;var pluginInfo = {
name: "Stats Plugin",
version: "1.0.0"
};
function onEnable() {
// Create database table
api.createTable("stats", "player_stats", {
"player_name": "TEXT PRIMARY KEY",
"kills": "INTEGER DEFAULT 0",
"deaths": "INTEGER DEFAULT 0"
});
// Register command
api.registerCommand("stats", "View your stats", "/stats [player]",
function(sender, args) {
var targetName = args.length > 0 ? args[0] : sender.getName();
var results = api.querySQL("stats",
"SELECT * FROM player_stats WHERE player_name = '" + targetName + "'");
if (results.length > 0) {
var stats = results[0];
api.sendMessage(sender, "&6=== Stats for " + targetName + " ===");
api.sendMessage(sender, "&eKills: &a" + stats.kills);
api.sendMessage(sender, "&eDeaths: &c" + stats.deaths);
} else {
api.sendMessage(sender, "&cNo stats found for " + targetName);
}
return true;
},
function(sender, args) {
// Tab completion
var players = api.getPlayerNames();
return players;
}
);
// Track kills
api.registerEvent("entity.PlayerDeathEvent", function(event) {
var killer = event.getEntity().getKiller();
var victim = event.getEntity();
if (killer) {
// Update killer stats
var killerStats = api.querySQL("stats",
"SELECT kills FROM player_stats WHERE player_name = '" + killer.getName() + "'");
if (killerStats.length > 0) {
api.updateData("stats", "player_stats",
{ "kills": killerStats[0].kills + 1 },
"player_name = '" + killer.getName() + "'");
} else {
api.insertData("stats", "player_stats", {
"player_name": killer.getName(),
"kills": 1,
"deaths": 0
});
}
}
// Update victim stats
var victimStats = api.querySQL("stats",
"SELECT deaths FROM player_stats WHERE player_name = '" + victim.getName() + "'");
if (victimStats.length > 0) {
api.updateData("stats", "player_stats",
{ "deaths": victimStats[0].deaths + 1 },
"player_name = '" + victim.getName() + "'");
} else {
api.insertData("stats", "player_stats", {
"player_name": victim.getName(),
"kills": 0,
"deaths": 1
});
}
});
}
this.onEnable = onEnable;
this.pluginInfo = pluginInfo;var pluginInfo = {
name: "Menu Plugin",
version: "1.0.0"
};
function onEnable() {
api.registerCommand("menu", "Open custom menu", "/menu", function(sender, args) {
var player = api.getPlayerFromSender(sender);
if (!player) {
api.sendMessage(sender, "&cOnly players can use this command!");
return false;
}
var inv = api.createInventory(null, 27, "&6Custom Menu");
// Fill with items
var item1 = api.createItemStack(api.getMaterial("DIAMOND"), 1);
item1 = api.setItemDisplayName(item1, "&bOption 1");
item1 = api.setItemLore(item1, ["&7Click me!"]);
api.setInventoryItem(inv, 10, item1);
var item2 = api.createItemStack(api.getMaterial("EMERALD"), 1);
item2 = api.setItemDisplayName(item2, "&aOption 2");
item2 = api.setItemLore(item2, ["&7Click me too!"]);
api.setInventoryItem(inv, 16, item2);
// Register click handler
api.registerInventoryClick(inv, function(event) {
event.setCancelled(true);
var slot = event.getSlot();
if (slot == 10) {
api.sendMessage(player, "&aYou clicked Option 1!");
api.giveItem(player, api.createItemStack(api.getMaterial("DIAMOND"), 5));
} else if (slot == 16) {
api.sendMessage(player, "&aYou clicked Option 2!");
api.giveItem(player, api.createItemStack(api.getMaterial("EMERALD"), 5));
}
});
player.openInventory(inv);
return true;
});
}
this.onEnable = onEnable;
this.pluginInfo = pluginInfo;| Command | Description | Permission |
|---|---|---|
/jsreload |
Reload all JS plugins | mcjs.admin |
/jsreload <plugin> |
Reload specific plugin | mcjs.admin |
/jslist |
List all loaded JS plugins | mcjs.admin |
api.registerCommand(name, executor)api.registerCommand(name, description, usage, executor)api.registerCommand(name, description, usage, executor, tabCompleter)
api.registerEvent(eventClassName, handler)api.registerEvent(eventClass, handler, priority)
api.runTask(task)api.runTaskLater(delay, task)api.runTaskTimer(delay, period, task)api.runTaskAsync(task)api.runTaskLaterAsync(delay, task)api.cancelTask(task)
api.getPlayer(name)- Get player by name (accepts string or object)api.getPlayerExact(name)- Get exact player match (accepts string or object)api.getPlayerFromSender(sender)- Convert CommandSender to Player (returns null if not a player)api.isPlayer(sender)- Check if CommandSender is a Playerapi.getOnlinePlayers()- Get all online playersapi.sendMessage(sender, message)- Send message to CommandSenderapi.sendTitle(player, title, subtitle)- Send title to playerapi.sendTitle(player, title, subtitle, fadeIn, stay, fadeOut)- Send title with custom timingsapi.sendActionBar(player, message)- Send action bar messageapi.broadcast(message)- Broadcast message to all playersapi.broadcast(message, permission)- Broadcast message to players with permissionapi.setHealth(player, health)- Set player healthapi.setFoodLevel(player, level)- Set player food levelapi.setSaturation(player, saturation)- Set player saturation level (float)api.getMaxHealth(player)- Get player's maximum healthapi.clearInventory(player)- Clear player's inventory (including armor)
api.createInventory(holder, size, title)- Create inventory with custom holderapi.inventory.createGUI(title, rows)- Create GUI using builder pattern (recommended)api.inventory.createInventoryWithHolder(holder, size, title)- Create inventory with custom holderapi.setInventoryItem(inventory, slot, item)- Set item in inventory slotapi.getInventoryItem(inventory, slot)- Get item from inventory slotapi.fillInventory(inventory, item)- Fill inventory with itemapi.registerInventoryClick(inventory, handler)- Register click handler for inventoryapi.registerInventoryClose(inventory, handler)- Register close handler for inventory
GUI Builder Example:
var gui = api.inventory.createGUI("&6My Menu", 3);
gui.setItem(10, item1).setItem(12, item2);
gui.onClick(function(event) { /* handler */ });
gui.onClose(function(event) { /* handler */ });
gui.open(player);api.createItemStack(material, amount)api.setItemDisplayName(item, name)api.setItemLore(item, lore)api.giveItem(player, item)api.getItemInMainHand(player)
api.createTable(dbName, tableName, columns)api.insertData(dbName, tableName, data)api.updateData(dbName, tableName, data, whereClause)api.deleteData(dbName, tableName, whereClause)api.querySQL(dbName, sql)api.countRows(dbName, tableName)api.countRows(dbName, tableName, whereClause)
api.saveYamlFile(fileName, data)api.loadYamlFile(fileName)api.saveJsonFile(fileName, content)api.loadJsonFile(fileName)api.saveTextFile(fileName, content)api.loadTextFile(fileName)
api.getPluginConfig(pluginName)api.savePluginConfig(pluginName, data)api.getPluginConfigValue(pluginName, path)api.getPluginConfigValue(pluginName, path, defaultValue)api.setPluginConfigValue(pluginName, path, value)
api.hasCooldown(playerName, key)api.setCooldown(playerName, key, durationMillis)api.getCooldownRemaining(playerName, key)api.removeCooldown(playerName, key)api.clearCooldowns(playerName)
api.createBossBar(title, color, style)api.createBossBar(title, colorName, styleName)
api.spawnParticle(location, particle, count)api.spawnParticle(location, particleName, count, offsetX, offsetY, offsetZ, extra)api.playSound(location, sound, volume, pitch)api.playSound(location, soundName, volume, pitch)
api.colorize(text)- Convert color codes to formatted textapi.stripColor(text)- Remove color codes from textapi.format(format, ...args)- Format string with argumentsapi.round(value, places)- Round number to decimal placesapi.clamp(value, min, max)- Clamp value between min and maxapi.getMaterial(materialName)- Get Material enum from stringapi.md5(input)- Generate MD5 hashapi.sha256(input)- Generate SHA-256 hashapi.base64Encode(input)- Encode string to Base64api.base64Decode(input)- Decode Base64 stringapi.getCurrentTimeMillis()- Get current timestamp in millisecondsapi.formatDate(timestamp)- Format timestamp to date stringapi.formatDate(timestamp, format)- Format timestamp with custom formatapi.getServerVersion()- Get server version stringapi.getMaxPlayers()- Get maximum player count
And many more! Check the source code for the complete list.
- Check the server console for errors
- Ensure the file has a
.jsextension - Verify the JavaScript syntax is correct
- Check that
onEnablefunction is defined
- Make sure you're using the correct event class name
- Check the event package (e.g.,
player.PlayerJoinEvent) - Verify the event handler function is correct
- Ensure the command is registered in
onEnable - Check for JavaScript errors in console
- Verify the command executor returns
trueorfalse
- Check the Issues page
- Create a new issue with your problem
- Include error logs and your plugin code
- Visit the Documentation Website for complete API reference
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
- ๐ Commit Generator: Use
python scripts/commit-generator.pyfor automatic commit messages - ๐ Issue Templates: Bug reports and feature requests templates included
- ๐ค Contributing: See CONTRIBUTING.md for guidelines
- ๐ Documentation: Complete API reference available at lootingvi.github.io/MC-JS
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Rhino JavaScript Engine
- Compatible with Paper, Spigot, and Bukkit
- Inspired by the need for easier plugin development
- Special thanks to the Minecraft plugin development community
Made with โค๏ธ for the Minecraft Community
โญ Star this repo โข ๐ Report Bug โข ๐ก Request Feature โข ๐ Documentation