šŸš€ Getting Started

Installation

Requirements

  • Minecraft Server: Paper/Spigot 1.20+
  • Java: Version 21 or higher
  • Minecraft Version: 1.20+
šŸ“¦ Steps:
  1. Download the latest release
  2. Place the JAR file in your server's plugins/ folder
  3. Start or restart your server
  4. Create JS plugins in plugins/MC-JS/js-plugins/

Your First Plugin

Create a new file in plugins/MC-JS/js-plugins/ with a .js extension:

my-first-plugin.js
// Plugin metadata (optional but recommended)
var pluginInfo = {
    name: "My First Plugin",
    version: "1.0.0",
    author: "YourName",
    description: "My awesome first plugin!"
};

// Get MC-JS version
var mcjsVersion = api.getMCJSVersion();
logger.info("Running on MC-JS " + mcjsVersion);

function onEnable() {
    logger.info("My plugin is enabled!");
    
    // Register a simple command
    api.registerCommand("hello", "Say hello", "/hello", function(sender, args) {
        api.sendMessage(sender, "&aHello from JavaScript!");
        return true;
    });
    
    // Register a player join 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 and metadata
this.onEnable = onEnable;
this.onDisable = onDisable;
this.pluginInfo = pluginInfo;
āœ… Done! Reload the plugin using /jsreload or restart the server, then test with /hello

šŸ“š Complete API Reference

šŸ’” Global Objects: These objects are available in all JavaScript plugins:
  • api - Complete JS API wrapper
  • server - Minecraft server instance
  • plugin - Main plugin instance
  • logger - Plugin logger (logger.info(), logger.warning(), etc.)
  • scheduler - Server scheduler
  • Bukkit - Bukkit API access

šŸ’¬ Command Registration

Simple Command

api.registerCommand("command", function(sender, args) {
    api.sendMessage(sender, "Command executed!");
    return true; // Return true for success, false for failure
});

Command with Description and Usage

api.registerCommand("command", "Description", "/command [args]", 
    function(sender, args) {
        if (args.length > 0) {
            api.sendMessage(sender, "You said: " + args[0]);
        } else {
            api.sendMessage(sender, "&cUsage: /command <message>");
        }
        return true;
    }
);

Command with Tab Completion

api.registerCommand("command", "Description", "/command [player]", 
    function(sender, args) {
        // Command executor
        var target = api.getPlayer(args[0]);
        if (target) {
            api.sendMessage(sender, "Found player: " + target.getName());
        }
        return true;
    },
    function(sender, args) {
        // Tab completer - return array of strings
        if (args.length === 1) {
            return api.getPlayerNames(); // Return all online player names
        }
        return [];
    }
);

šŸŽ® Event Registration

Basic Event

api.registerEvent("player.PlayerJoinEvent", function(event) {
    var player = event.getPlayer();
    api.sendMessage(player, "&aWelcome to the server!");
});

Event with Priority

api.registerEvent("block.BlockBreakEvent", function(event) {
    var player = event.getPlayer();
    var block = event.getBlock();
    
    // Cancel the event
    event.setCancelled(true);
    
    api.sendMessage(player, "&cYou cannot break blocks here!");
}, "HIGH"); // Priorities: LOWEST, LOW, NORMAL, HIGH, HIGHEST, MONITOR

Available Event Packages

Package Examples
player.* PlayerJoinEvent, PlayerQuitEvent, PlayerDeathEvent
block.* BlockBreakEvent, BlockPlaceEvent, BlockClickEvent
entity.* EntityDamageEvent, EntitySpawnEvent
inventory.* InventoryClickEvent, InventoryCloseEvent
server.* ServerLoadEvent, PluginEnableEvent

ā° Task Scheduling

Delayed Task

// Run after 5 seconds (100 ticks = 5 seconds, 20 ticks = 1 second)
api.runTaskLater(100, function() {
    logger.info("This runs after 5 seconds");
    api.broadcast("&a5 seconds have passed!");
});

Repeating Task

// Run every minute (1200 ticks = 60 seconds)
var task = api.runTaskTimer(0, 1200, function() {
    var playerCount = api.getOnlinePlayers().size();
    logger.info("Server has " + playerCount + " player(s) online");
});

// Cancel the task later
api.runTaskLater(6000, function() {
    api.cancelTask(task);
    logger.info("Task cancelled after 5 minutes");
});

Async Task

// Run in a separate thread (for non-blocking operations)
api.runTaskAsync(function() {
    // This won't block the main server thread
    var response = api.httpGet("https://api.example.com/data");
    logger.info("Fetched data: " + response);
    
    // Update something on the main thread
    api.runTask(function() {
        api.broadcast("&aData fetched successfully!");
    });
});
Method Description
api.runTask(task) Run task on next tick
api.runTaskLater(delay, task) Run task after delay (in ticks)
api.runTaskTimer(delay, period, task) Run repeating task
api.runTaskAsync(task) Run task asynchronously
api.runTaskLaterAsync(delay, task) Run delayed async task
api.cancelTask(task) Cancel a running task

šŸ‘¤ Player Management

Get Players

// Get player by name
var player = api.getPlayer("PlayerName");
if (player) {
    api.sendMessage(player, "Hello!");
}

// Get exact player name (case-sensitive)
var exactPlayer = api.getPlayerExact("ExactPlayerName");

// Get all online players
var allPlayers = api.getOnlinePlayers();
for (var i = 0; i < allPlayers.length; i++) {
    var p = allPlayers[i];
    logger.info("Online: " + p.getName());
}

Send Messages

// Send message to player
api.sendMessage(player, "&aHello &bWorld!");

// Broadcast to all players
api.broadcast("&6Server announcement!");

// Broadcast with permission
api.broadcast("&cAdmin message!", "admin.message");

// Send title
api.sendTitle(player, "&aWelcome!", "&eEnjoy your stay!", 10, 70, 20);

// Send action bar
api.sendActionBar(player, "&bYou are in the spawn area!");

Player Properties

// Health
api.setHealth(player, 20.0);
var health = api.getHealth(player);
var maxHealth = api.getMaxHealth(player);

// Food
api.setFoodLevel(player, 20);
var food = api.getFoodLevel(player);
api.setSaturation(player, 5.0);

// Inventory
api.clearInventory(player); // Clear player inventory (including armor)

// Game Mode
api.setGameMode(player, GameMode.CREATIVE);
var gamemode = api.getGameMode(player);

// Teleportation
var location = api.createLocation(world, 100, 64, 200);
api.teleport(player, location);

// Experience
api.setLevel(player, 10);
api.setExp(player, 0.5);
api.giveExp(player, 100);

šŸ“¦ Inventory & Items

Create Inventory

// Create custom inventory (27 = 3 rows)
var inv = api.createInventory(null, 27, "&6My Custom GUI");

// Create inventory by type
var chest = api.createInventory(null, InventoryType.CHEST, "&bChest Menu");

// Create custom inventory (simplified, no holder needed)
var customInv = api.createCustomInventory(27, "&6Custom GUI");

// Create custom inventory with rows (1-6 rows)
var gui = api.createCustomInventory(3, "&bMy Menu", true); // 3 rows = 27 slots

GUI Builder Pattern (Recommended)

// Create a GUI builder for easy inventory creation
var gui = api.createGUI("&6My Custom Menu", 3); // 3 rows

// Set items with slot-specific click handlers
var item1 = api.createItemStack(api.getMaterial("DIAMOND"), 1);
item1 = api.setItemDisplayName(item1, "&bDiamond Option");
gui.setItem(10, item1, function(event) {
    event.setCancelled(true);
    api.sendMessage(event.getWhoClicked(), "&aYou clicked the diamond!");
});

var item2 = api.createItemStack(api.getMaterial("EMERALD"), 1);
item2 = api.setItemDisplayName(item2, "&aEmerald Option");
gui.setItem(16, item2, function(event) {
    event.setCancelled(true);
    api.sendMessage(event.getWhoClicked(), "&aYou clicked the emerald!");
});

// Fill borders with background item
var background = api.createItemStack(api.getMaterial("GRAY_STAINED_GLASS_PANE"), 1);
background = api.setItemDisplayName(background, " ");
gui.fillBorders(background);

// Set background for empty slots
gui.setBackground(background);

// Prevent items from being removed from the GUI (default: false)
gui.setAllowItemRemoval(false);

// Global click handler (called for all clicks)
gui.onClick(function(event) {
    // This runs for every click, even if slot has specific handler
    var slot = event.getSlot();
    // Do something...
});

// Close handler
gui.onClose(function(event) {
    var player = event.getPlayer();
    api.sendMessage(player, "&7You closed the menu");
});

// Build and open for player
var inventory = gui.buildAndOpen(player);

// Or build without opening
var inv = gui.build();
player.openInventory(inv);

Create Items with Enchantments

// Create item stack
var sword = api.createItemStack(api.getMaterial("DIAMOND_SWORD"), 1);

// Set display name
sword = api.setItemDisplayName(sword, "&b&lMagic Sword");

// Add enchantments
api.addEnchantment(sword, "SHARPNESS", 5);
api.addEnchantment(sword, "FIRE_ASPECT", 2);
api.addEnchantment(sword, "UNBREAKING", 3);

// Check enchantments
if (api.hasEnchantment(sword, "SHARPNESS")) {
    var level = api.getEnchantmentLevel(sword, "SHARPNESS");
    logger.info("Sword has Sharpness level " + level);
}

// Get all enchantments
var enchantments = api.getEnchantments(sword);
for (var enchantName in enchantments) {
    logger.info(enchantName + ": " + enchantments[enchantName]);
}

// Remove enchantment
api.removeEnchantment(sword, "FIRE_ASPECT");

// Set lore
sword = api.setItemLore(sword, [
    "&7This is a special diamond",
    "&7given by the JS plugin!",
    "&eRight-click to use"
]);

// Give item to player
api.giveItem(player, diamond);

// Set item in inventory slot
api.setInventoryItem(inv, 0, diamond);

// Fill entire inventory
api.fillInventory(inv, diamond);

// Fill range
api.fillInventoryRange(inv, diamond, 0, 8);

Inventory Events

// Register click handler
api.registerInventoryClick(inv, function(event) {
    event.setCancelled(true); // Prevent item movement
    
    var slot = event.getSlot();
    var player = event.getWhoClicked();
    
    api.sendMessage(player, "&aYou clicked slot " + slot);
    
    if (slot === 10) {
        // Do something when slot 10 is clicked
        api.giveItem(player, api.createItemStack(api.getMaterial("DIAMOND"), 5));
    }
});

// Register close handler
api.registerInventoryClose(inv, function(event) {
    var player = event.getPlayer();
    api.sendMessage(player, "&7You closed the inventory");
});

Custom Inventory Holder

// Create a custom inventory holder for advanced usage
var holder = api.createInventoryHolder();

// Store custom data in the holder
holder.setData("menuType", "shop");
holder.setData("player", player);

// Create inventory with custom holder
var inv = api.createInventoryWithHolder(holder, 27, "&6Shop Menu");

// Get holder from inventory
var invHolder = api.getInventoryHolder(inv);
if (invHolder) {
    var menuType = invHolder.getData("menuType");
    var storedPlayer = invHolder.getData("player");
}

// Check if inventory has custom holder
if (api.isCustomInventory(inv)) {
    // This is a custom inventory
}

// Get handlers from custom inventory
var slotHandler = api.getSlotClickHandler(inv, 10);
var globalHandler = api.getGlobalClickHandler(inv);

šŸ—„ļø Database Operations

Create Table

api.createTable("mydb", "players", {
    "id": "INTEGER PRIMARY KEY AUTOINCREMENT",
    "name": "TEXT NOT NULL",
    "level": "INTEGER DEFAULT 1",
    "coins": "INTEGER DEFAULT 0",
    "last_seen": "INTEGER"
});

Insert Data

api.insertData("mydb", "players", {
    "name": "PlayerName",
    "level": 10,
    "coins": 1000,
    "last_seen": api.getCurrentTimeMillis()
});

Update Data

api.updateData("mydb", "players", 
    { "level": 20, "coins": 2000 },
    "name = 'PlayerName'"
);

Query Data

var results = api.querySQL("mydb", 
    "SELECT * FROM players WHERE level > 5"
);

for (var i = 0; i < results.length; i++) {
    var row = results[i];
    logger.info("Player: " + row.name + ", Level: " + row.level);
}

Delete Data

// Delete specific rows
api.deleteData("mydb", "players", "last_seen < " + (api.getCurrentTimeMillis() - 86400000));

// Count rows
var count = api.countRows("mydb", "players");
var activeCount = api.countRows("mydb", "players", "last_seen > " + (api.getCurrentTimeMillis() - 86400000));

šŸŒ World & Block Management

World Operations

// Get worlds
var world = api.getWorld("world");
var allWorlds = api.getWorlds();

// Time control
api.setWorldTime(world, 6000); // Set to noon
var time = api.getWorldTime(world);

// Weather control
api.setStorm(world, true);
api.setThundering(world, true);
var hasStorm = api.hasStorm(world);
var isThundering = api.isThundering(world);

// World border
var border = api.getWorldBorder(world);
api.setWorldBorderSize(world, 1000);
api.setWorldBorderCenter(world, 0, 0);

Block Operations

// Get block
var block = api.getBlock(location);
var blockType = api.getBlockType(location);

// Set block
api.setBlockType(location, api.getMaterial("DIAMOND_BLOCK"));

// Break block
api.breakBlock(location);
api.breakBlock(location, false); // Don't drop items

// Get block at coordinates
var block = api.getBlockAt(world, 100, 64, 200);

// Get blocks in radius
var blocks = api.getBlocksInRadius(centerLocation, 5.0);
for (var i = 0; i < blocks.length; i++) {
    logger.info("Block: " + blocks[i].getType().name());
}

Explosions & Lightning

// Create explosion
api.createExplosion(location, 5.0); // Power 5
api.createExplosion(location, 5.0, true); // With fire

// Strike lightning
api.strikeLightning(location);
api.strikeLightningEffect(location); // Visual only, no damage

šŸŽÆ Entity Management

Spawn Entities

// Spawn entity (string or enum)
var zombie = api.spawnEntity(location, "ZOMBIE");
var creeper = api.spawnEntity(location, EntityType.CREEPER);

// Entity properties
api.setEntityCustomName(zombie, "&cCustom Zombie");
api.setEntityGlowing(zombie, true);
api.setEntityGravity(zombie, false);
api.setEntityInvulnerable(zombie, true);

// Remove entity
api.removeEntity(zombie);

šŸ“ File Operations

YAML Files

// Save YAML
var data = {
    "server": {
        "name": "My Server",
        "maxPlayers": 100
    },
    "settings": {
        "pvp": true,
        "spawnProtection": true
    }
};
api.saveYamlFile("config", data);

// Load YAML
// Note: loadYamlFile returns a Java Map object, use .get() to access values
var config = api.loadYamlFile("config");
var serverName = config.get("server").get("name");

// For simple values, you can use direct access (works in Rhino)
var serverName2 = config.get("server").get("name");

// Working with arrays in YAML:
var bansData = api.loadYamlFile("bansystem_bans");
var bansList = bansData.get("bans"); // Java List
var nextId = bansData.get("nextId");

// Convert Java List to JavaScript array if needed
var bans = [];
if (bansList instanceof java.util.List) {
    for (var i = 0; i < bansList.size(); i++) {
        var banObj = bansList.get(i);
        // Convert Java Map to JavaScript object
        var ban = {};
        if (banObj instanceof java.util.Map) {
            var keys = banObj.keySet().toArray();
            for (var j = 0; j < keys.length; j++) {
                var key = keys[j];
                ban[key] = banObj.get(key);
            }
        }
        bans.push(ban);
    }
}

JSON Files

// Save JSON
var jsonData = {
    "players": ["Player1", "Player2"],
    "timestamp": api.getCurrentTimeMillis()
};
api.saveJsonFile("data", JSON.stringify(jsonData, null, 2));

// Load JSON
var json = api.loadJsonFile("data");
var data = JSON.parse(json);

Text Files

// Save text
api.saveTextFile("log", "Line 1\nLine 2\nLine 3");

// Load text
var content = api.loadTextFile("log");
logger.info("File content: " + content);

Config System

// Get plugin config
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"
});

ā±ļø Cooldown System

Basic Cooldown

// 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: " + Math.ceil(remaining / 1000) + " seconds");
}

// Remove cooldown
api.removeCooldown(player.getName(), "command");

// Clear all cooldowns for player
api.clearCooldowns(player.getName());

šŸŽØ BossBar

Create and Manage BossBar

// Create boss bar (with enum or string)
var bossBar = api.createBossBar("&cBoss Fight!", "RED", "SOLID");
// Or: api.createBossBar("&cBoss Fight!", BarColor.RED, BarStyle.SOLID);

// Add players
bossBar.addPlayer(player);
bossBar.addAll(api.getOnlinePlayers());

// Update progress (0.0 to 1.0)
bossBar.setProgress(0.5); // 50%

// Update title
bossBar.setTitle("&6New Title");

// Remove players
bossBar.removePlayer(player);
bossBar.removeAll();

// Available colors: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
// Available styles: SOLID, SEGMENTED_6, SEGMENTED_10, SEGMENTED_12, SEGMENTED_20

šŸ’” Complete Examples

Example 1: Welcome Plugin

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 after 1 second
        api.runTaskLater(20, function() {
            var bread = api.createItemStack(api.getMaterial("BREAD"), 5);
            var sword = api.createItemStack(api.getMaterial("WOODEN_SWORD"), 1);
            api.giveItem(player, bread);
            api.giveItem(player, sword);
            api.sendMessage(player, "&aYou received a welcome kit!");
        });
    });
}

this.onEnable = onEnable;
this.pluginInfo = pluginInfo;

Example 2: Stats Plugin with Database

var pluginInfo = {
    name: "Stats Plugin",
    version: "1.0.0"
};

function onEnable() {
    // Create database
    api.createTable("stats", "player_stats", {
        "player_name": "TEXT PRIMARY KEY",
        "kills": "INTEGER DEFAULT 0",
        "deaths": "INTEGER DEFAULT 0",
        "playtime": "INTEGER DEFAULT 0"
    });
    
    // Stats 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);
                api.sendMessage(sender, "&ePlaytime: &b" + Math.floor(stats.playtime / 3600) + " hours");
            } else {
                api.sendMessage(sender, "&cNo stats found for " + targetName);
            }
            
            return true;
        },
        function(sender, args) {
            return api.getPlayerNames();
        }
    );
    
    // Track player deaths
    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;

Example 3: Custom GUI Menu (Old Method)

var pluginInfo = {
    name: "Menu Plugin",
    version: "1.0.0"
};

function onEnable() {
    api.registerCommand("menu", "Open custom menu", "/menu", function(sender, args) {
        if (!(sender instanceof Player)) {
            api.sendMessage(sender, "&cOnly players can use this command!");
            return false;
        }
        
        var player = sender;
        var inv = api.createInventory(null, 27, "&6Custom Menu");
        
        // Option 1
        var item1 = api.createItemStack(api.getMaterial("DIAMOND"), 1);
        item1 = api.setItemDisplayName(item1, "&bOption 1");
        item1 = api.setItemLore(item1, ["&7Click to get diamonds!"]);
        api.setInventoryItem(inv, 10, item1);
        
        // Option 2
        var item2 = api.createItemStack(api.getMaterial("EMERALD"), 1);
        item2 = api.setItemDisplayName(item2, "&aOption 2");
        item2 = api.setItemLore(item2, ["&7Click to get emeralds!"]);
        api.setInventoryItem(inv, 16, item2);
        
        // Close button
        var close = api.createItemStack(api.getMaterial("BARRIER"), 1);
        close = api.setItemDisplayName(close, "&cClose Menu");
        api.setInventoryItem(inv, 22, close);
        
        // 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));
            } else if (slot === 22) {
                player.closeInventory();
            }
        });
        
        player.openInventory(inv);
        return true;
    });
}

this.onEnable = onEnable;
this.pluginInfo = pluginInfo;

Example 4: GUI Builder Pattern (Recommended)

var pluginInfo = {
    name: "Shop Plugin",
    version: "1.0.0"
};

function onEnable() {
    api.registerCommand("shop", "Open shop", "/shop", function(sender, args) {
        if (!(sender instanceof Player)) {
            api.sendMessage(sender, "&cOnly players can use this command!");
            return false;
        }
        
        var player = sender;
        
        // Create GUI with builder pattern
        var gui = api.createGUI("&6&lShop Menu", 3);
        
        // Background item
        var bg = api.createItemStack(api.getMaterial("GRAY_STAINED_GLASS_PANE"), 1);
        bg = api.setItemDisplayName(bg, " ");
        gui.fillBorders(bg);
        gui.setBackground(bg);
        
        // Shop item 1 - Diamond Sword
        var sword = api.createItemStack(api.getMaterial("DIAMOND_SWORD"), 1);
        sword = api.setItemDisplayName(sword, "&bDiamond Sword");
        sword = api.setItemLore(sword, [
            "&7Price: &a100 Coins",
            "&7Click to purchase!"
        ]);
        gui.setItem(10, sword, function(event) {
            event.setCancelled(true);
            api.sendMessage(player, "&aYou purchased a Diamond Sword!");
            api.giveItem(player, sword);
        });
        
        // Shop item 2 - Golden Apple
        var apple = api.createItemStack(api.getMaterial("GOLDEN_APPLE"), 1);
        apple = api.setItemDisplayName(apple, "&eGolden Apple");
        apple = api.setItemLore(apple, [
            "&7Price: &a50 Coins",
            "&7Click to purchase!"
        ]);
        gui.setItem(13, apple, function(event) {
            event.setCancelled(true);
            api.sendMessage(player, "&aYou purchased a Golden Apple!");
            api.giveItem(player, apple);
        });
        
        // Shop item 3 - Enchanted Book
        var book = api.createItemStack(api.getMaterial("ENCHANTED_BOOK"), 1);
        book = api.setItemDisplayName(book, "&dEnchanted Book");
        book = api.setItemLore(book, [
            "&7Price: &a200 Coins",
            "&7Click to purchase!"
        ]);
        gui.setItem(16, book, function(event) {
            event.setCancelled(true);
            api.sendMessage(player, "&aYou purchased an Enchanted Book!");
            api.giveItem(player, book);
        });
        
        // Close button
        var close = api.createItemStack(api.getMaterial("BARRIER"), 1);
        close = api.setItemDisplayName(close, "&cClose Shop");
        gui.setItem(22, close, function(event) {
            event.setCancelled(true);
            player.closeInventory();
        });
        
        // Global click handler (optional)
        gui.onClick(function(event) {
            // Log all clicks for debugging
            // api.sendMessage(player, "&7Debug: Clicked slot " + event.getSlot());
        });
        
        // Close handler
        gui.onClose(function(event) {
            api.sendMessage(player, "&7Shop closed. Come back soon!");
        });
        
        // Build and open
        gui.buildAndOpen(player);
        return true;
    });
}

this.onEnable = onEnable;
this.pluginInfo = pluginInfo;

Example 4: Advanced Cooldown System

var pluginInfo = {
    name: "Cooldown Example",
    version: "1.0.0"
};

function onEnable() {
    api.registerCommand("heal", "Heal yourself", "/heal", function(sender, args) {
        if (!(sender instanceof Player)) {
            api.sendMessage(sender, "&cOnly players can use this!");
            return false;
        }
        
        var player = sender;
        var cooldownKey = "heal";
        
        if (api.hasCooldown(player.getName(), cooldownKey)) {
            var remaining = api.getCooldownRemaining(player.getName(), cooldownKey);
            api.sendMessage(player, "&cCooldown: " + Math.ceil(remaining / 1000) + " seconds");
            return false;
        }
        
        // Heal player
        api.setHealth(player, api.getMaxHealth(player));
        api.setFoodLevel(player, 20);
        api.sendMessage(player, "&aYou have been healed!");
        
        // Set 30 second cooldown
        api.setCooldown(player.getName(), cooldownKey, 30000);
        
        return true;
    });
}

this.onEnable = onEnable;
this.pluginInfo = pluginInfo;

Example 5: BossBar Progress Bar

var pluginInfo = {
    name: "BossBar Example",
    version: "1.0.0"
};

var bossBars = {};

function onEnable() {
    api.registerEvent("player.PlayerJoinEvent", function(event) {
        var player = event.getPlayer();
        
        // Create boss bar
        var bossBar = api.createBossBar("&aLoading...", "GREEN", "SOLID");
        bossBar.addPlayer(player);
        bossBars[player.getName()] = bossBar;
        
        // Animate progress
        var progress = 0;
        var task = api.runTaskTimer(0, 5, function() {
            if (!bossBars[player.getName()]) {
                api.cancelTask(task);
                return;
            }
            
            progress += 0.02;
            if (progress > 1) {
                progress = 1;
                bossBar.setTitle("&aWelcome!");
                api.cancelTask(task);
                
                // Remove after 3 seconds
                api.runTaskLater(60, function() {
                    bossBar.removeAll();
                    delete bossBars[player.getName()];
                });
            } else {
                bossBar.setProgress(progress);
                bossBar.setTitle("&aLoading... " + Math.floor(progress * 100) + "%");
            }
        });
    });
    
    api.registerEvent("player.PlayerQuitEvent", function(event) {
        var player = event.getPlayer();
        if (bossBars[player.getName()]) {
            bossBars[player.getName()].removeAll();
            delete bossBars[player.getName()];
        }
    });
}

this.onEnable = onEnable;
this.pluginInfo = pluginInfo;

Example 6: World Protection System

var pluginInfo = {
    name: "World Protection",
    version: "1.0.0"
};

var protectedRegions = [];

function onEnable() {
    // Define protected region (spawn area)
    var spawn = api.getWorlds()[0].getSpawnLocation();
    protectedRegions.push({
        center: spawn,
        radius: 50
    });
    
    // Protect blocks
    api.registerEvent("block.BlockBreakEvent", function(event) {
        var player = event.getPlayer();
        var block = event.getBlock();
        
        if (isInProtectedRegion(block.getLocation())) {
            if (!api.hasPermission(player, "protection.bypass")) {
                event.setCancelled(true);
                api.sendMessage(player, "&cYou cannot break blocks in the protected area!");
                api.playSound(player, "ENTITY_VILLAGER_NO", 1.0, 1.0);
            }
        }
    });
    
    api.registerEvent("block.BlockPlaceEvent", function(event) {
        var player = event.getPlayer();
        var block = event.getBlock();
        
        if (isInProtectedRegion(block.getLocation())) {
            if (!api.hasPermission(player, "protection.bypass")) {
                event.setCancelled(true);
                api.sendMessage(player, "&cYou cannot place blocks in the protected area!");
            }
        }
    });
    
    // Visual indicator
    api.registerEvent("player.PlayerMoveEvent", function(event) {
        var player = event.getPlayer();
        if (isInProtectedRegion(player.getLocation())) {
            api.sendActionBar(player, "&c⚠ Protected Area");
        }
    });
}

function isInProtectedRegion(location) {
    for (var i = 0; i < protectedRegions.length; i++) {
        var region = protectedRegions[i];
        var distance = api.getDistance(location, region.center);
        if (distance <= region.radius) {
            return true;
        }
    }
    return false;
}

this.onEnable = onEnable;
this.pluginInfo = pluginInfo;

šŸ“‹ Complete API Reference

All Available Methods

This is a comprehensive list of all API methods available in MC-JS. Click on a category to expand:

⚔Command Methods (3 methods)

ā–¼
MethodDescription
api.registerCommand(name, executor)Register simple command
api.registerCommand(name, desc, usage, executor)Register command with description
api.registerCommand(name, desc, usage, executor, completer)Register command with tab completion

šŸŽÆEvent Methods (6 methods)

ā–¼
MethodDescription
api.registerEvent(eventClassName, handler)Register event by string name
api.registerEvent(eventClass, handler, priority)Register event with priority
api.registerInventoryClick(inv, handler)Register inventory click handler
api.registerInventoryClose(inv, handler)Register inventory close handler
api.createCustomInventory(size, title)Create custom inventory
api.createGUI(title, rows)Create GUI builder

ā±ļøTask Methods (8 methods)

ā–¼
MethodDescription
api.runTask(task)Run task on next tick
api.runTaskLater(delay, task)Run task after delay (ticks)
api.runTaskTimer(delay, period, task)Run repeating task
api.runTaskAsync(task)Run async task
api.runTaskLaterAsync(delay, task)Run delayed async task
api.runTaskSafe(task, onError)Run task with error handler
api.runTaskAsyncSafe(task, onError)Run async task with error handler
api.cancelTask(task)Cancel running task

šŸ‘¤Player Methods (30+ methods)

ā–¼
MethodDescription
api.getPlayer(name)Get player by name
api.getPlayerExact(name)Get player by exact name
api.getOnlinePlayers()Get all online players
api.getPlayerNames()Get list of online player names
api.sendMessage(sender, message)Send message
api.sendTitle(player, title, subtitle)Send title
api.sendActionBar(player, message)Send action bar
api.broadcast(message)Broadcast to all
api.broadcast(message, permission)Broadcast with permission
api.setHealth(player, health)Set player health
api.getHealth(player)Get player health
api.getMaxHealth(player)Get max health
api.setFoodLevel(player, level)Set food level
api.getFoodLevel(player)Get food level
api.setSaturation(player, saturation)Set saturation
api.getSaturation(player)Get saturation
api.clearInventory(player)Clear player inventory (including armor)
api.damage(player, damage)Damage player
api.setExp(player, exp)Set experience (0.0-1.0)
api.getExp(player)Get experience (0.0-1.0)
api.setLevel(player, level)Set level
api.getLevel(player)Get level
api.giveExp(player, amount)Give experience points
api.setGameMode(player, mode)Set game mode
api.getGameMode(player)Get game mode
api.teleport(player, location)Teleport player
api.teleport(entity, location)Teleport entity
api.getPlayerByUUID(uuid)Get player by UUID
api.getOfflinePlayer(name)Get offline player
api.getOfflinePlayerByUUID(uuid)Get offline player by UUID
api.isPlayerOnline(name/player)Check if player is online
api.kickPlayer(player, reason)Kick player
api.banPlayer(name, reason)Ban player
api.unbanPlayer(name)Unban player
api.isBanned(name)Check if banned
api.addToWhitelist(name)Add to whitelist
api.removeFromWhitelist(name)Remove from whitelist
api.isWhitelisted(name)Check if whitelisted
api.setWhitelistEnabled(enabled)Enable/disable whitelist
api.isWhitelistEnabled()Check if whitelist enabled
api.generateOfflineUUID(name)Generate UUID for offline player
Permission Methods
MethodDescription
api.hasPermission(sender, permission)Check permission (CommandSender)
api.hasPermission(player, permission)Check permission (Player)
api.addPermission(player, permission)Add permission to player
api.removePermission(player, permission)Remove permission from player
api.isOp(player)Check if player is OP
api.setOp(player, op)Set player OP status
Player Data Methods
MethodDescription
api.setPlayerMetadata(player, key, value)Set player metadata
api.getPlayerMetadata(player, key)Get player metadata
api.hasPlayerMetadata(player, key)Check if player has metadata
api.removePlayerMetadata(player, key)Remove player metadata

šŸ“¦Inventory & GUI Methods (20+ methods)

ā–¼
Basic Inventory Methods
MethodDescription
api.createInventory(holder, size, title)Create inventory
api.createCustomInventory(size, title)Create custom inventory (simplified)
api.createCustomInventory(rows, title, useRows)Create custom inventory by rows
api.createGUI(title, rows)Create GUI builder (recommended)
api.createInventoryHolder()Create custom inventory holder
api.createInventoryWithHolder(holder, size, title)Create inventory with custom holder
api.getInventoryHolder(inv)Get holder from inventory
api.isCustomInventory(inv)Check if custom inventory
api.getSlotClickHandler(inv, slot)Get slot-specific click handler
api.getGlobalClickHandler(inv)Get global click handler
api.setInventoryItem(inv, slot, item)Set item in slot
GUI Builder Methods
MethodDescription
gui.setItem(slot, item)Set item at slot
gui.setItem(slot, item, handler)Set item with click handler
gui.setItems(startSlot, endSlot, item)Set items in range
gui.fill(item)Fill entire inventory
gui.fillBorders(item)Fill borders only
gui.setBackground(item)Set background for empty slots
gui.onClick(handler)Set global click handler
gui.onClose(handler)Set close handler
gui.setAllowItemRemoval(allow)Allow/prevent items from being removed from GUI (default: false)
gui.build()Build and return inventory
gui.buildAndOpen(player)Build and open for player
Custom Inventory Holder Methods
MethodDescription
holder.setData(key, value)Store custom data
holder.getData(key)Get custom data
holder.getAllData()Get all custom data
holder.removeData(key)Remove custom data
holder.clearData()Clear all custom data
Custom Inventory Management
MethodDescription
api.updateInventoryItem(inv, slot, item)Update item in custom inventory
api.getInventoryItem(inv, slot)Get item from custom inventory
api.clearInventory(inventory)Clear custom inventory (GUI)
api.refreshInventory(inv)Refresh/rebuild custom inventory
api.isInventorySlotEmpty(inv, slot)Check if slot is empty
api.getFirstEmptySlot(inv)Get first empty slot
api.addItemToInventory(inv, item)Add item to inventory
api.removeItemFromInventory(inv, item)Remove item from inventory
api.inventoryContains(inv, item)Check if inventory contains item
api.inventoryContainsAtLeast(inv, item, amount)Check if inventory has at least amount

āš”ļøItem Methods (15+ methods)

ā–¼
MethodDescription
api.setInventoryItem(inv, slot, item)Set item in slot
api.getInventoryItem(inv, slot)Get item from slot
api.fillInventory(inv, item)Fill entire inventory
api.fillInventoryRange(inv, item, start, end)Fill inventory range
api.createItemStack(material, amount)Create item stack
api.setItemDisplayName(item, name)Set item display name
api.setItemLore(item, lore)Set item lore
api.giveItem(player, item)Give item to player
api.getItemInMainHand(player)Get item in main hand
api.setItemInMainHand(player, item)Set item in main hand
api.getItemInOffHand(player)Get item in off hand
api.setItemInOffHand(player, item)Set item in off hand
api.getItemMeta(item)Get item meta
api.setItemMeta(item, meta)Set item meta
Enchantment Methods
MethodDescription
api.addEnchantment(item, name, level)Add enchantment to item
api.removeEnchantment(item, name)Remove enchantment from item
api.hasEnchantment(item, name)Check if item has enchantment
api.getEnchantmentLevel(item, name)Get enchantment level
api.getEnchantments(item)Get all enchantments (returns Map)

šŸ’¾Database Methods (7 methods)

ā–¼
MethodDescription
api.createTable(dbName, tableName, columns)Create database table
api.insertData(dbName, tableName, data)Insert data
api.updateData(dbName, tableName, data, where)Update data
api.deleteData(dbName, tableName, where)Delete data
api.querySQL(dbName, sql)Query database
api.countRows(dbName, tableName)Count rows
api.executeSQL(dbName, sql)Execute SQL

šŸ“File Methods (9 methods)

ā–¼
MethodDescription
api.saveYamlFile(fileName, data)Save YAML file (data as Map/Object)
api.loadYamlFile(fileName)Load YAML file (returns Java Map, use .get() to access values)
api.saveJsonFile(fileName, content)Save JSON file
api.loadJsonFile(fileName)Load JSON file (returns JSON string)
api.saveTextFile(fileName, content)Save text file
api.loadTextFile(fileName)Load text file (returns string)

Important: loadYamlFile returns a Java Map object. Use .get(key) to access values. Arrays are returned as Java List objects. Use instanceof java.util.List to check and convert to JavaScript arrays if needed. See the YAML Files section above for examples.

āš™ļøConfig Methods (4 methods)

ā–¼
MethodDescription
api.getPluginConfig(pluginName)Get plugin config
api.savePluginConfig(pluginName, data)Save plugin config
api.getPluginConfigValue(pluginName, path)Get config value
api.setPluginConfigValue(pluginName, path, value)Set config value

ā³Cooldown Methods (5 methods)

ā–¼
MethodDescription
api.hasCooldown(playerName, key)Check cooldown
api.setCooldown(playerName, key, duration)Set cooldown (ms)
api.getCooldownRemaining(playerName, key)Get remaining time
api.removeCooldown(playerName, key)Remove cooldown
api.clearCooldowns(playerName)Clear all cooldowns

BossBar Methods

MethodDescription
api.createBossBar(title, color, style)Create boss bar
api.createBossBar(title, colorName, styleName)Create boss bar (strings)

Particle & Sound Methods

MethodDescription
api.spawnParticle(loc, particle, count)Spawn particles
api.spawnParticle(loc, particleName, count, ...)Spawn particles (string)
api.playSound(loc, sound, volume, pitch)Play sound at location
api.playSound(player, sound, volume, pitch)Play sound for player
api.playSound(loc, soundName, volume, pitch)Play sound (string)

šŸŒWorld & Block Methods (20+ methods)

ā–¼
MethodDescription
api.getWorld(name)Get world by name
api.getWorlds()Get all worlds
api.getWorldNames()Get world names
api.getWorldTime(world)Get world time
api.setWorldTime(world, time)Set world time
api.hasStorm(world)Check if storming
api.setStorm(world, storm)Set storm
api.isThundering(world)Check if thundering
api.setThundering(world, thundering)Set thundering
api.getBlock(location)Get block
api.getBlockType(location)Get block type
api.setBlockType(location, material)Set block type
api.breakBlock(location)Break block
api.breakBlock(location, dropItems)Break block with drop option
api.getBlocksInRadius(center, radius)Get blocks in radius
api.createExplosion(location, power)Create explosion
api.createExplosion(location, power, setFire)Create explosion with fire
api.strikeLightning(location)Strike lightning
api.strikeLightningEffect(location)Strike lightning effect
World Management Methods
MethodDescription
api.setWorldDifficulty(world, difficulty)Set world difficulty (PEACEFUL, EASY, NORMAL, HARD)
api.getWorldDifficulty(world)Get world difficulty
api.setWorldPVP(world, pvp)Enable/disable PvP
api.isWorldPVP(world)Check if PvP enabled
api.setWorldSpawnLocation(world, location)Set world spawn location
api.getWorldSpawnLocation(world)Get world spawn location
api.setWorldKeepSpawnInMemory(world, keepLoaded)Keep spawn in memory
api.isWorldKeepSpawnInMemory(world)Check if spawn kept in memory
api.setWorldAutoSave(world, autoSave)Enable/disable auto-save
api.isWorldAutoSave(world)Check if auto-save enabled
api.getWorldEnvironment(world)Get world environment
api.getWorldSeed(world)Get world seed

šŸ‘¾Entity Methods (15+ methods)

ā–¼
MethodDescription
api.spawnEntity(location, type)Spawn entity
api.spawnEntity(location, typeName)Spawn entity (string)
api.getNearbyEntities(location, x, y, z)Get nearby entities
api.setEntityCustomName(entity, name)Set entity name
api.setEntityGlowing(entity, glowing)Set glowing
api.setEntityGravity(entity, gravity)Set gravity
api.setEntityInvulnerable(entity, invulnerable)Set invulnerable
api.setEntityAI(entity, ai)Enable/disable AI
api.hasEntityAI(entity)Check if entity has AI
api.setEntitySilent(entity, silent)Set entity silent
api.isEntitySilent(entity)Check if entity is silent
api.setEntityCollidable(entity, collidable)Set entity collidable
api.isEntityCollidable(entity)Check if entity is collidable
api.getEntityLocation(entity)Get entity location
api.teleportEntity(entity, location)Teleport entity to location
api.teleportEntity(entity, target)Teleport entity to target entity
api.removeEntity(entity)Remove entity

Scoreboard Methods

MethodDescription
api.getMainScoreboard()Get main scoreboard
api.createScoreboard()Create new scoreboard
api.getTeam(scoreboard, name)Get team
api.createTeam(scoreboard, name)Create team
api.getObjective(scoreboard, name)Get objective
api.createObjective(scoreboard, name, criteria, displayName)Create objective

Potion Effect Methods

MethodDescription
api.createPotionEffect(type, duration, amplifier)Create potion effect
api.createPotionEffect(type, duration, amplifier, ambient)Create potion effect with ambient

Location & Vector Methods

MethodDescription
api.createLocation(world, x, y, z)Create location
api.createLocation(world, x, y, z, yaw, pitch)Create location with rotation
api.createVector(x, y, z)Create vector
api.getDistance(loc1, loc2)Get distance between locations
api.getDistanceSquared(loc1, loc2)Get squared distance
api.getMidpoint(loc1, loc2)Get midpoint between locations

Material Methods

MethodDescription
api.getMaterial(name)Get material by name
api.isBlock(material)Check if material is block
api.isItem(material)Check if material is item

World Border Methods

MethodDescription
api.getWorldBorder(world)Get world border
api.setWorldBorderSize(world, size)Set world border size
api.setWorldBorderCenter(world, x, z)Set world border center

Advancement Methods

MethodDescription
api.grantAdvancement(player, key)Grant advancement
api.revokeAdvancement(player, key)Revoke advancement

Server & Plugin Methods

MethodDescription
api.getMCJSVersion()Get MC-JS plugin version
api.getServerVersion()Get server version
api.getBukkitVersion()Get Bukkit version
api.getMaxPlayers()Get max players
api.getMotd()Get MOTD
api.isPluginEnabled(name)Check if plugin enabled
api.getPlugin(name)Get plugin
api.hasEconomy()Check if economy available

Logging Methods

MethodDescription
api.logInfo(message)Log info message
api.logWarning(message)Log warning message
api.logError(message)Log error message

Random Methods

MethodDescription
api.randomInt(min, max)Random integer
api.randomDouble(min, max)Random double
api.randomBoolean()Random boolean

String Utility Methods

MethodDescription
api.format(format, ...args)Format string
api.join(delimiter, ...elements)Join strings

Utility Methods

MethodDescription
api.colorize(text)Colorize text
api.stripColor(text)Strip colors
api.format(format, ...args)Format string
api.round(value, places)Round number
api.clamp(value, min, max)Clamp value
api.md5(input)MD5 hash
api.sha256(input)SHA256 hash
api.base64Encode(input)Base64 encode
api.base64Decode(input)Base64 decode
api.urlEncode(input)URL encode
api.urlDecode(input)URL decode
api.getCurrentTimeMillis()Get current time (milliseconds)
api.getCurrentTime()Get current time (seconds)
api.formatDate(timestamp)Format date
api.formatDate(timestamp, pattern)Format date with pattern
api.parseDate(dateString)Parse date
api.parseDate(dateString, pattern)Parse date with pattern
api.executeCommand(command)Execute console command
api.httpGet(url)HTTP GET request
api.httpPost(url, data)HTTP POST request

Validation Methods

MethodDescription
api.isValidPlayer(player)Check if valid online player
api.isValidLocation(location)Check if valid location
api.isValidWorld(world)Check if valid world
api.isValidItemStack(item)Check if valid item stack
api.isValidEntity(entity)Check if valid entity
api.isValidInventory(inventory)Check if valid inventory

File Existence Methods

MethodDescription
api.yamlFileExists(fileName)Check if YAML file exists
api.jsonFileExists(fileName)Check if JSON file exists
api.textFileExists(fileName)Check if text file exists
api.deleteYamlFile(fileName)Delete YAML file
api.deleteJsonFile(fileName)Delete JSON file
api.deleteTextFile(fileName)Delete text file

Database Methods (Additional)

MethodDescription
api.countRows(dbName, tableName)Count rows in table
api.countRows(dbName, tableName, where)Count rows with condition
api.updateData(dbName, tableName, data, where)Update data
api.deleteData(dbName, tableName, where)Delete data

šŸ”§ Advanced Topics

BossBar

// Create boss bar
var bossBar = api.createBossBar("&cBoss Fight!", "RED", "SOLID");
bossBar.addPlayer(player);
bossBar.setProgress(0.5); // 50%

// Update progress
var progress = 0;
var task = api.runTaskTimer(0, 20, function() {
    progress += 0.01;
    if (progress > 1) progress = 0;
    bossBar.setProgress(progress);
    bossBar.setTitle("&cBoss: " + Math.floor(progress * 100) + "%");
});

// Remove after 10 seconds
api.runTaskLater(200, function() {
    bossBar.removeAll();
    api.cancelTask(task);
});

Permissions & Player Management

// Check permissions
if (api.hasPermission(player, "myplugin.use")) {
    // Player has permission
}

// Add/remove permissions dynamically
api.addPermission(player, "myplugin.vip");
api.removePermission(player, "myplugin.vip");

// OP management
if (api.isOp(player)) {
    api.sendMessage(player, "You are OP!");
}
api.setOp(player, true);

// Player metadata
api.setPlayerMetadata(player, "lastLogin", api.getCurrentTimeMillis());
var lastLogin = api.getPlayerMetadata(player, "lastLogin");
if (api.hasPlayerMetadata(player, "lastLogin")) {
    logger.info("Last login: " + lastLogin);
}

// Whitelist management
api.addToWhitelist("PlayerName");
api.removeFromWhitelist("PlayerName");
if (api.isWhitelisted("PlayerName")) {
    logger.info("Player is whitelisted");
}
api.setWhitelistEnabled(true);

Particles & Sounds

// 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);

// Particle effect at player location
api.registerEvent("player.PlayerMoveEvent", function(event) {
    var player = event.getPlayer();
    if (player.isSprinting()) {
        api.spawnParticle(player.getLocation(), "CLOUD", 3, 0, 0, 0);
    }
});

Entity Manipulation

// Spawn entity
var zombie = api.spawnEntity(location, "ZOMBIE");

// Set properties
api.setEntityCustomName(zombie, "&cBoss Zombie");
api.setEntityGlowing(zombie, true);
api.setEntityGravity(zombie, false);
api.setEntityInvulnerable(zombie, true);

// AI control
api.setEntityAI(zombie, false); // Disable AI
if (!api.hasEntityAI(zombie)) {
    logger.info("Zombie has no AI");
}

// Silent and collidable
api.setEntitySilent(zombie, true);
api.setEntityCollidable(zombie, false);

// Teleport entity
var targetLocation = api.createLocation(world, 100, 64, 200);
api.teleportEntity(zombie, targetLocation);

// Or teleport to another entity
api.teleportEntity(zombie, player);

// Get entity location
var entityLoc = api.getEntityLocation(zombie);

// Remove entity
api.removeEntity(zombie);

World Management

// Difficulty
api.setWorldDifficulty(world, "HARD");
var difficulty = api.getWorldDifficulty(world);

// PvP
api.setWorldPVP(world, true);
if (api.isWorldPVP(world)) {
    logger.info("PvP is enabled");
}

// Spawn location
var spawnLoc = api.createLocation(world, 0, 64, 0);
api.setWorldSpawnLocation(world, spawnLoc);
var currentSpawn = api.getWorldSpawnLocation(world);

// Auto-save
api.setWorldAutoSave(world, false);
if (!api.isWorldAutoSave(world)) {
    logger.info("Auto-save disabled");
}

// World info
var environment = api.getWorldEnvironment(world);
var seed = api.getWorldSeed(world);
logger.info("World: " + world.getName() + ", Seed: " + seed);

Validation & Error Handling

// Validate objects before use
var player = api.getPlayer("PlayerName");
if (api.isValidPlayer(player)) {
    // Safe to use player
    api.sendMessage(player, "Hello!");
} else {
    logger.warning("Player not found or offline");
}

// Validate locations
var location = api.createLocation(world, 0, 64, 0);
if (api.isValidLocation(location)) {
    api.teleport(player, location);
}

// Validate items
var item = api.getItemInMainHand(player);
if (api.isValidItemStack(item)) {
    api.addEnchantment(item, "SHARPNESS", 1);
}

// Safe async tasks with error handling
api.runTaskAsyncSafe(function() {
    // Async work here
    var data = api.loadYamlFile("data");
    // Process data...
}, function(error) {
    // Error handler
    logger.severe("Error in async task: " + error);
});

HTTP Requests

// GET request
api.runTaskAsync(function() {
    var response = api.httpGet("https://api.example.com/data");
    logger.info("Response: " + response);
    
    // Parse JSON response
    var data = JSON.parse(response);
    
    // Update on main thread
    api.runTask(function() {
        api.broadcast("&aData fetched: " + data.message);
    });
});

// POST request
api.runTaskAsync(function() {
    var jsonData = JSON.stringify({
        "player": "PlayerName",
        "action": "join"
    });
    var response = api.httpPost("https://api.example.com/log", jsonData);
    logger.info("POST response: " + response);
});

Utility Methods

// String utilities
var colored = api.colorize("&aHello &bWorld");
var stripped = api.stripColor(colored);
var formatted = api.format("Hello %s! You have %d items.", "Player", 5);

// 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 World");
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 customFormat = api.formatDate(now, "dd.MM.yyyy HH:mm");
var parsed = api.parseDate("2024-01-01 12:00:00");

// Distance
var distance = api.getDistance(loc1, loc2);
var midpoint = api.getMidpoint(loc1, loc2);
āš ļø Important:
  • Always wrap code in try-catch blocks for error handling
  • Use async tasks for HTTP requests and heavy operations
  • Clean up resources (cancel tasks, close connections) in onDisable
  • Use cooldowns to prevent spam
  • Cache frequently accessed data

✨ Best Practices

Error Handling

// Always wrap risky operations in try-catch
api.registerCommand("risky", "Risky command", "/risky", function(sender, args) {
    try {
        var player = api.getPlayer(args[0]);
        if (!player) {
            api.sendMessage(sender, "&cPlayer not found!");
            return false;
        }
        
        // Risky operation
        player.teleport(someLocation);
        api.sendMessage(sender, "&aSuccess!");
        return true;
    } catch (e) {
        logger.severe("Error in risky command: " + e);
        api.sendMessage(sender, "&cAn error occurred!");
        return false;
    }
});

// Event handlers should also have error handling
api.registerEvent("player.PlayerJoinEvent", function(event) {
    try {
        var player = event.getPlayer();
        // Your code here
    } catch (e) {
        logger.severe("Error in PlayerJoinEvent: " + e);
        // Don't let errors break the event system
    }
});

Performance Optimization

// āŒ BAD: Checking player every tick
api.runTaskTimer(0, 1, function() {
    var players = api.getOnlinePlayers();
    for (var i = 0; i < players.length; i++) {
        // Heavy operation
    }
});

// āœ… GOOD: Cache data and check less frequently
var cachedPlayers = [];
api.runTaskTimer(0, 20, function() { // Every second instead of every tick
    cachedPlayers = api.getOnlinePlayers();
    for (var i = 0; i < cachedPlayers.length; i++) {
        // Heavy operation
    }
});

// āœ… GOOD: Use async for heavy operations
api.runTaskAsync(function() {
    var data = api.httpGet("https://api.example.com/heavy");
    // Process data...
    
    // Update on main thread
    api.runTask(function() {
        api.broadcast("Data loaded!");
    });
});

// āœ… GOOD: Cancel tasks when not needed
var task = api.runTaskTimer(0, 20, function() {
    // Do something
});

// Later, when done:
api.cancelTask(task);

Code Organization

// āœ… GOOD: Organized plugin structure
var pluginInfo = {
    name: "My Plugin",
    version: "1.0.0"
};

// Configuration
var config = {
    enabled: true,
    cooldown: 5000
};

// Data storage
var playerData = {};

// Helper functions
function formatMessage(player, message) {
    return "&7[" + player.getName() + "] &f" + message;
}

function hasPermission(player, perm) {
    return api.hasPermission(player, "myplugin." + perm);
}

// Main functions
function onEnable() {
    logger.info("Loading " + pluginInfo.name + " v" + pluginInfo.version);
    
    // Initialize
    initializeDatabase();
    registerCommands();
    registerEvents();
    
    logger.info(pluginInfo.name + " enabled!");
}

function onDisable() {
    // Cleanup
    saveData();
    logger.info(pluginInfo.name + " disabled!");
}

// Exports
this.onEnable = onEnable;
this.onDisable = onDisable;
this.pluginInfo = pluginInfo;

Player Safety Checks

// Always check if player is online before using
api.registerEvent("player.PlayerQuitEvent", function(event) {
    var player = event.getPlayer();
    var playerName = player.getName();
    
    // Save data before player leaves
    savePlayerData(playerName);
});

// Check if player exists before operations
function giveReward(playerName) {
    var player = api.getPlayer(playerName);
    if (!player) {
        logger.warning("Player " + playerName + " is not online!");
        return false;
    }
    
    // Safe to use player object
    api.giveItem(player, reward);
    return true;
}

// Use instanceof to check player type
api.registerCommand("playeronly", "Player only command", "/playeronly", function(sender, args) {
    if (!(sender instanceof Player)) {
        api.sendMessage(sender, "&cThis command can only be used by players!");
        return false;
    }
    
    var player = sender;
    // Safe to use player methods
    player.teleport(location);
    return true;
});

šŸ”§ Troubleshooting

Common Issues & Solutions

Plugin not loading

  • Check file extension: Must be .js
  • Check syntax: Use a JavaScript validator
  • Check console: Look for error messages
  • Check onEnable: Function must be defined and exported
  • Check file location: Must be in plugins/MC-JS/js-plugins/

Events not firing

  • Check event name: Use format player.PlayerJoinEvent
  • Check registration: Must be in onEnable
  • Check handler: Function must accept event parameter
  • Check console: Look for registration errors
  • Try reload: Use /jsreload <plugin>

Commands not working

  • Check registration: Must be in onEnable
  • Check return value: Must return true or false
  • Check permissions: Use api.hasPermission()
  • Check console: Look for JavaScript errors
  • Try reload: Use /jsreload <plugin>

Player is null errors

  • Always check: if (player) { ... }
  • Use instanceof: if (sender instanceof Player)
  • Check online status: api.getPlayer(name) returns null if offline
  • Handle offline players: Use server.getOfflinePlayer() for UUID

Database errors

  • Check table exists: Use api.createTable() first
  • Check data types: Match column types
  • Check NOT NULL: Provide values for required fields
  • Check SQL syntax: Use proper SQL in queries
  • Check file permissions: Database files need write access

Performance issues

  • Avoid heavy operations: Use async tasks
  • Cache data: Don't query database every tick
  • Cancel tasks: Clean up repeating tasks
  • Limit operations: Don't process all players every tick
  • Use cooldowns: Prevent spam

Debug Mode

Enable debug mode in config.yml to see detailed logging:

settings:
  debug-mode: true

This will show:

  • Event registration details
  • Event execution logs
  • Plugin loading information
  • Command execution details

Common Error Messages

"ReferenceError: X is not defined"

Solution: Check if the variable/class is available. Use instanceof Player instead of checking for undefined properties.

"Cannot read property 'X' of null"

Solution: Always check if objects exist before accessing properties:

var player = api.getPlayer(name);
if (player) {
    player.teleport(location);
}

"SQLiteException: NOT NULL constraint failed"

Solution: Ensure all required fields are provided when inserting data:

api.insertData("db", "table", {
    "required_field": value, // Must not be null
    "optional_field": value || null
});

"Events being called multiple times"

Solution: This is fixed in MC-JS - events are only dispatched once per event instance, even if multiple plugins register the same event.

ā“ Frequently Asked Questions

General Questions

Q: Can I use ES6+ features?

A: Yes! MC-JS uses Rhino with ES6 support. You can use arrow functions, let/const, template literals, etc.

Q: How do I reload my plugin?

A: Use /jsreload <plugin-name> or /jsreload to reload all plugins.

Q: Can I use npm packages?

A: No, MC-JS uses Rhino which doesn't support Node.js modules. Use the built-in API instead.

Q: How do I check if a player is online?

A: Use api.getPlayer(name) - it returns null if the player is offline.

Q: Can I access Bukkit API directly?

A: Yes! The server object gives you access to the Bukkit Server instance. Also, Bukkit classes like Player, Material, etc. are available.

Q: How do I handle offline players?

A: Use server.getOfflinePlayer(name) to get UUID and other data for offline players.

Q: Can multiple plugins register the same event?

A: Yes! Each plugin's handler will be called. MC-JS ensures events are only dispatched once per event instance.

Q: How do I create a config file?

A: Use api.getPluginConfig(pluginName) and api.savePluginConfig(pluginName, data).

Q: Can I use async/await?

A: No, but you can use api.runTaskAsync() for asynchronous operations.

Q: How do I prevent command spam?

A: Use the cooldown system: api.setCooldown(playerName, "command", 5000)

šŸ“‹ Common Code Snippets

Player Utilities

// Check if player is online
function isPlayerOnline(name) {
    return api.getPlayer(name) !== null;
}

// Get player safely
function getPlayerSafe(name) {
    var player = api.getPlayer(name);
    if (!player) {
        logger.warning("Player " + name + " is not online!");
        return null;
    }
    return player;
}

// Get UUID for online or offline player
function getPlayerUUID(name) {
    var player = api.getPlayer(name);
    if (player) {
        return player.getUniqueId().toString();
    }
    try {
        var offline = server.getOfflinePlayer(name);
        return offline.getUniqueId().toString();
    } catch (e) {
        return null;
    }
}

Command Patterns

// Command with permission check
api.registerCommand("admin", "Admin command", "/admin", function(sender, args) {
    if (!api.hasPermission(sender, "myplugin.admin")) {
        api.sendMessage(sender, "&cNo permission!");
        return false;
    }
    
    if (!(sender instanceof Player)) {
        api.sendMessage(sender, "&cPlayers only!");
        return false;
    }
    
    // Command logic
    return true;
});

// Command with cooldown
api.registerCommand("cooldown", "Cooldown command", "/cooldown", function(sender, args) {
    if (!(sender instanceof Player)) return false;
    
    var player = sender;
    var playerName = player.getName();
    
    if (api.hasCooldown(playerName, "cooldown")) {
        var remaining = api.getCooldownRemaining(playerName, "cooldown");
        api.sendMessage(player, "&cCooldown: " + Math.ceil(remaining / 1000) + "s");
        return false;
    }
    
    api.setCooldown(playerName, "cooldown", 5000);
    api.sendMessage(player, "&aCommand executed!");
    return true;
});

Data Storage Patterns

// Save player data
var playerData = {};

function savePlayerData(playerName) {
    try {
        api.saveYamlFile("players/" + playerName, playerData[playerName] || {});
    } catch (e) {
        logger.severe("Error saving data for " + playerName + ": " + e);
    }
}

function loadPlayerData(playerName) {
    try {
        var data = api.loadYamlFile("players/" + playerName);
        if (data) {
            // Convert Java Map to JavaScript object
            var playerObj = {};
            if (data instanceof java.util.Map) {
                var keys = data.keySet().toArray();
                for (var i = 0; i < keys.length; i++) {
                    var key = keys[i];
                    var value = data.get(key);
                    // Handle nested Maps
                    if (value instanceof java.util.Map) {
                        var nestedObj = {};
                        var nestedKeys = value.keySet().toArray();
                        for (var j = 0; j < nestedKeys.length; j++) {
                            nestedObj[nestedKeys[j]] = value.get(nestedKeys[j]);
                        }
                        playerObj[key] = nestedObj;
                    } else {
                        playerObj[key] = value;
                    }
                }
            }
            playerData[playerName] = playerObj;
        }
    } catch (e) {
        logger.warning("No data found for " + playerName);
        playerData[playerName] = {};
    }
}

// Save on quit
api.registerEvent("player.PlayerQuitEvent", function(event) {
    var player = event.getPlayer();
    savePlayerData(player.getName());
    delete playerData[player.getName()];
});

GUI Patterns

// Simple GUI with GUI Builder
function openMenu(player) {
    var gui = api.createGUI("&6Menu", 3);
    
    // Background
    var bg = api.createItemStack(api.getMaterial("GRAY_STAINED_GLASS_PANE"), 1);
    bg = api.setItemDisplayName(bg, " ");
    gui.fillBorders(bg);
    
    // Items
    var item1 = api.createItemStack(api.getMaterial("DIAMOND"), 1);
    item1 = api.setItemDisplayName(item1, "&bOption 1");
    gui.setItem(10, item1, function(event) {
        event.setCancelled(true);
        api.sendMessage(player, "&aYou clicked Option 1!");
    });
    
    gui.buildAndOpen(player);
}

// GUI with data storage
var guiData = {};

function openShop(player) {
    var gui = api.createGUI("&6Shop", 3);
    var playerName = player.getName();
    
    guiData[playerName] = {
        page: 1,
        category: "weapons"
    };
    
    // Store data in holder
    var inv = gui.build();
    var holder = api.getInventoryHolder(inv);
    if (holder) {
        holder.setData("player", playerName);
        holder.setData("page", 1);
    }
    
    player.openInventory(inv);
}