メインコンテンツまでスキップ

Luaスクリプティング:はじめに

This page teaches you to write your first Lua script for a FreeMinecraftModels prop or custom item, from an empty file all the way to a working interactive script. By the end you will understand hooks, context, the prop and item APIs, and the general shape of every script file.

Once you are comfortable with the basics, continue with the companion pages:

  • Prop & Item API -- the context.prop, context.item, context.event, context.world, and other context APIs
  • Examples & Patterns -- complete working scripts for props and items you can study and adapt
  • Troubleshooting -- common errors, debugging tips, and the QC checklist
Experimental Feature

Lua prop and item scripts are currently experimental. Hook names, helper methods, and behavior can still change as FreeMinecraftModels evolves, so test carefully before using them on a production server.

Relationship To EliteMobs Lua

FreeMinecraftModels shares the same Lua engine (Magmacore) as EliteMobs. If you already write Lua powers for EliteMobs, the core concepts -- hooks, context, api_version, scheduler, zones, and the sandbox -- are identical. The difference is:

  • EliteMobs scripts run on bosses and have hooks like on_boss_damaged_by_player, on_enter_combat, etc.
  • FMM prop scripts run on props and have hooks like on_right_click, on_left_click, on_projectile_hit, etc.
  • FMM item scripts run on custom items and have hooks like on_equip, on_attack_entity, on_consume, on_game_tick, etc.

The context.world, context.zones, context.scheduler, context.state, and context.log APIs are the same across both plugins. This page covers what is specific to FMM props and items.


What Prop Scripts Are

Prop scripts are standalone .lua files that live in the plugins/FreeMinecraftModels/scripts/ folder. They are referenced from a YAML config file that sits alongside the model file, and they run whenever the prop is spawned into the world.

What Prop Scripts Are Good At

Prop scripts shine when you need:

  • Interactive props that respond to player clicks (doors, levers, buttons)
  • Invulnerable decorative props that cannot be broken by players
  • Proximity triggers that detect when players enter or leave an area
  • Animated props that play animations on interaction or on a timer
  • Sound-emitting props that play sounds when clicked or approached
  • Any prop behavior that requires logic beyond static decoration

If your prop is purely decorative and needs no interaction, you do not need a script.


What Item Scripts Are

Item scripts use the same .lua file format and the same scripts/ folder as prop scripts. The difference is that they are attached to custom items -- models that have a material: field set in their YML config file. While prop scripts run when a prop entity is spawned in the world, item scripts run when a player equips the custom item (main hand, off hand, or armor slot) and stop when the item is unequipped.

How Item Scripts Work

  • Activation: A script instance is created when a player equips a custom FMM item. Scripts are per-player per-item-type -- one ScriptInstance per (player, itemId) pair.
  • Deactivation: The script instance is destroyed when the item is unequipped (moved out of the active slot, dropped, or the player disconnects).
  • Item identification: Custom items are identified by the fmm_item_id PDC (PersistentDataContainer) key, which is different from the prop's model_id. To get a properly tagged item, use /fmm giveitem <id> or the admin menu.
  • Context: Item hooks receive context with context.player, context.item, context.world, context.state, context.scheduler, context.log, and (where applicable) context.event.

What Item Scripts Are Good At

Item scripts shine when you need:

  • Custom weapons with special abilities (frost swords, magic wands)
  • Tools with unique right-click or shift-click actions
  • Consumable items with custom effects
  • Armor with passive effects while worn
  • Items that track usage or have limited durability
  • Any held-item behavior beyond vanilla mechanics

Who This Page Is For

This page is written for three kinds of readers:

  • Someone who already knows EliteMobs Lua scripting and wants to learn the FMM-specific hooks and APIs
  • Someone who is new to Lua scripting and needs a complete, exact-name reference for props
  • Someone using AI to draft prop scripts and needing enough detail to tell when the AI invented something fake

You do not need to become a full Lua developer before writing useful prop scripts. For most practical prop scripts, the only things you really need are:

  • How to place a valid hook in the returned table
  • How to read values from context
  • How to stop early with if ... then return end
  • How to call a few helper methods exactly

簡単なLua入門

You don't need to be a Lua expert to write FMM scripts. Most scripts only use a handful of concepts: variables (local x = 5), functions (function foo() end), if checks (if x then ... end), tables ({key = value}), and nil (Lua's "nothing" value). The syntax is lightweight — no semicolons, no curly braces, just end to close blocks.

For a complete walkthrough with examples, see the MagmaCore Lua Scripting Engine — Tiny Lua Primer. That primer is shared across all Nightbreak plugins, so learning it once applies everywhere.


Where Files Live

Script Files

Place .lua files in the central scripts folder:

plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua

FMM discovers all .lua files in plugins/FreeMinecraftModels/scripts/ at startup.

Model Files and Config Files

Each model file can have a sibling .yml config file in the same directory:

plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- script config for torch_01
scripts/
invulnerable.lua <-- referenced by torch_01.yml

The .yml config is what connects a model to its scripts.


Config File Format

The YAML config file that sits next to a model file has these fields:

isEnabled: true
voxelize: false
solidify: false
scripts:
- invulnerable.lua

For custom items (models that players can hold or equip), you also set the material field and optionally name, lore, and enchantments:

isEnabled: true
material: DIAMOND_SWORD
name: "&bFrost Blade"
lore:
- "&7A sword forged in eternal ice"
- "&7Slows enemies on hit"
enchantments:
- "SHARPNESS,5"
- "UNBREAKING,3"
scripts:
- frost_sword.lua
FieldTypeDefaultNotes
isEnabledbooleantrueWhether scripts are active for this prop/item
scriptslist of strings[]Filenames of .lua scripts in the scripts/ folder
voxelizebooleanfalseSnap placement to 90-degree rotation and block grid alignment
solidifybooleanfalsePlace packet-only barrier blocks in the prop's footprint (requires voxelize)
materialstring""A valid Bukkit Material name (e.g. DIAMOND_SWORD). Setting this turns the model into a custom item that players can hold or equip, which activates the item scripting system
namestring""Display name for the custom item. Supports & color codes
lorelist of strings[]Lore lines shown in the item tooltip. Supports & color codes
enchantmentslist of strings[]Enchantments applied to the item. Format: "ENCHANTMENT_NAME,LEVEL" (e.g. "SHARPNESS,5")

You can attach multiple scripts to the same prop. Each script is its own independent instance.

Item ID and Script Limit
  • The item ID is derived from the YML filename without its extension. For example, frost_sword.yml produces an item ID of frost_sword. This is the ID used by /fmm giveitem and the fmm_item_id PDC key.
  • Items only use the first script in the scripts: list. Unlike props, which run all listed scripts as independent instances, custom items ignore any scripts after the first entry.
  • The .lua extension is auto-appended to script filenames if you omit it, so frost_sword and frost_sword.lua are equivalent in the scripts: list.

Lazy Config Generation

When a prop spawns and no sibling .yml file exists, FMM automatically creates a default config file with isEnabled: true and an empty scripts: list. This happens asynchronously, so the prop will not have scripts on its first spawn -- only after the config is created and you edit it to add script filenames.

This means:

  1. Place your model file in models/
  2. Spawn the prop once (FMM creates the .yml automatically)
  3. Edit the generated .yml to add your script filenames
  4. Respawn the prop or reload (scripts are now active)

フックリファレンス

Every Lua prop script file returns a table. Each key in that table (besides api_version and priority) must be one of the hooks listed below. The runtime calls the matching function whenever the corresponding game event fires.

HookFires whenNotes
on_spawnThe prop is spawned into the worldRuns once when the script is bound
on_game_tickOnce every server tick (50 ms)Only active if the script defines this hook
on_destroyThe prop is removed from the worldCleanup hook
on_left_clickA player left-clicks (hits) the propcontext.event is the damage event
on_right_clickA player right-clicks the propcontext.event is the interaction event
on_projectile_hitA projectile hits the propcontext.event is the projectile hit event
on_zone_enterA player enters a watched zoneRequires a zone watch to be set up
on_zone_leaveA player leaves a watched zoneRequires a zone watch to be set up

アイテムフックリファレンス

Item scripts return a table just like prop scripts, with api_version = 1 and hook functions. The following hooks are available for item scripts. All item hooks receive context with context.player, context.item, and (where applicable) context.event.

Combat Hooks

HookFires whenNotes
on_attack_entityThe player attacks an entity while holding the itemcontext.event is the damage event
on_kill_entityThe player kills an entity while holding the itemcontext.event is the death event
on_take_damageThe player takes damage while the item is equippedcontext.event is the damage event
on_shield_blockThe player blocks damage with a shieldcontext.event is the damage event
on_shoot_bowThe player shoots a bowcontext.event is the bow shoot event
on_projectile_hitA projectile shot by the player hits somethingcontext.event is the projectile hit event
on_projectile_launchThe player launches a projectilecontext.event is the projectile launch event

Interaction Hooks

HookFires whenNotes
on_right_clickThe player right-clicks while holding the itemcontext.event is the interact event
on_left_clickThe player left-clicks while holding the itemcontext.event is the interact event
on_shift_right_clickThe player shift+right-clicks while holding the itemcontext.event is the interact event
on_shift_left_clickThe player shift+left-clicks while holding the itemcontext.event is the interact event
on_interact_entityThe player right-clicks an entity while holding the itemcontext.event is the entity interact event

Equipment Hooks

HookFires whenNotes
on_equipThe item is equipped (moved into an active slot)Good place to initialize state
on_unequipThe item is unequipped (moved out of an active slot)Good place to clean up
on_swap_handsThe player swaps the item between main and off handcontext.event is the swap event
on_dropThe player drops the itemcontext.event is the drop event

Utility Hooks

HookFires whenNotes
on_break_blockThe player breaks a block while holding the itemcontext.event is the block break event
on_consumeThe player consumes the item (food/potion)context.event is the consume event
on_item_damageThe item takes durability damagecontext.event is the item damage event
on_fishThe player uses a fishing rodcontext.event is the fish event
on_deathThe player dies while the item is equippedcontext.event is the death event

Lifecycle Hook

HookFires whenNotes
on_game_tickEvery server tick while the item is equippedUse sparingly -- runs 20 times per second

最小ファイル規約

Every Lua prop script must return a table.

Required and Optional Top-Level Fields

FieldRequiredTypeNotes
api_versionYesNumberCurrently must be 1
priorityNoNumberExecution priority. Lower values run first. Defaults to 0
supported hook keysNoFunctionMust use one of the exact hook names listed in the Hook Reference

Validation Rules

  • The file must return a table.
  • api_version is required and must currently be 1.
  • priority must be numeric if present.
  • Every extra top-level key must be a supported hook name.
  • Every hook key must point to a function.
  • Unknown top-level keys are rejected.

Helper functions and local constants should live above the final return, not inside the returned table.


Your First Working Prop Script, Built Slowly

Before Step 1: Set Up the Config

  1. Place your model file (e.g. my_prop.fmmodel) in plugins/FreeMinecraftModels/models/
  2. Spawn the prop once to generate the .yml config
  3. Create your script file in plugins/FreeMinecraftModels/scripts/first_test.lua
  4. Edit plugins/FreeMinecraftModels/models/my_prop.yml:
isEnabled: true
scripts:
- first_test.lua
  1. Respawn the prop or reload the server

Step 1: Make the File Load

return {
api_version = 1,

on_spawn = function(context)
end
}

If this loads without errors in the console, you proved:

  • The file is valid Lua
  • FMM found it in the scripts/ folder
  • The config correctly references it
  • The returned table shape is correct

Step 2: Make the Prop Do One Visible Thing

return {
api_version = 1,

on_spawn = function(context)
context.log:info("Prop script loaded for: " .. (context.prop.model_id or "unknown"))
end
}

Check the server console. If you see the log message, your hook is firing.

Step 3: React to a Player Click

return {
api_version = 1,

on_right_click = function(context)
context.log:info("Prop was right-clicked!")
end
}

Right-click the prop in-game. If the console shows the message, the click hook is working.

Step 4: Cancel Damage to Make the Prop Invulnerable

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

This is the pattern used by the premade invulnerable.lua script. It cancels the damage event so the prop's backing armor stand cannot be destroyed.

Step 5: Play an Animation on Click

return {
api_version = 1,

on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}

This plays the "open" animation on the prop model, blended and non-looping.


What Is context?

Every hook function receives one argument called context. Think of it as a toolbox that FMM hands you each time something happens -- it contains everything you need to interact with the prop, the world, zones, and more.

You don't create context yourself -- FMM creates it and passes it to your hook. For full details on the shared context APIs (context.state, context.log, context.scheduler, context.world, context.zones), see the MagmaCore Lua Scripting Engine page.


Key context APIs

Here is a summary of what is available. For full details, see Prop API.

  • context.prop -- (Prop scripts only) The prop entity. Provides model_id, current_location, play_animation(), and stop_animation().

  • context.item -- (Item scripts only) The custom item. Provides id, material(), get_amount(), set_amount(), consume(), get_uses(), set_uses(), get_name(), set_name(), get_lore(), set_lore(), get_durability(), get_durability_percentage(), use_durability(), and use_durability_percentage(). See Prop & Item API for full details.

  • context.player -- (Item scripts only) The player holding the item. Provides all player entity fields and methods plus UI methods like show_boss_bar(), show_action_bar(), and show_title().

  • context.event -- The Bukkit event that triggered this hook. Available in click, combat, and interaction hooks. Provides cancel(), uncancel(), and is_cancelled. Is nil in hooks that have no event (like on_spawn, on_game_tick, and on_equip).

  • context.state -- A plain Lua table that persists for the script instance's lifetime. See context.state.

  • context.log -- Console logging. See context.log.

  • context.scheduler -- Delayed and repeating tasks. See context.scheduler.

  • context.world -- World interaction: particles, sounds, block queries, lightning, nearby entities. See context.world.

  • context.zones -- Create and watch spatial zones (spheres, cylinders, cuboids). See context.zones.


メソッド構文::.

For an explanation of : vs . method syntax in Lua, see the MagmaCore Lua Scripting Engine page. Both forms are accepted by the FMM API.


コピペ用スターターテンプレート

Smallest Valid Prop Script

return {
api_version = 1,

on_spawn = function(context)
end
}

Invulnerable Prop Template

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

Interactive Prop Template

return {
api_version = 1,

on_spawn = function(context)
context.state.is_active = false
end,

on_right_click = function(context)
context.state.is_active = not context.state.is_active

if context.state.is_active then
context.prop:play_animation("activate", true, true)
else
context.prop:stop_animation()
end
end
}

Smallest Valid Item Script

return {
api_version = 1,

on_equip = function(context)
end
}

Item with Right-Click Action Template

return {
api_version = 1,

on_equip = function(context)
context.state.on_cooldown = false
end,

on_right_click = function(context)
if context.state.on_cooldown then return end

-- Your action here
context.player:send_message("&aItem activated!")

-- Set cooldown
context.state.on_cooldown = true
context.scheduler:run_later(40, function()
context.state.on_cooldown = false
end)
end
}

Larger File Layout

local ANIMATION_NAME = "idle"

local function do_something(context)
context.log:info("Doing something!")
end

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
context.state.task_id = nil
end,

on_right_click = function(context)
do_something(context)
end,

on_destroy = function(context)
if context.state.task_id ~= nil then
context.scheduler:cancel(context.state.task_id)
end
end
}

最初の実際のワークフロー

When building a brand-new prop script, use this order:

  1. Create the .lua file and make on_spawn work.
  2. Add the script filename to the prop's .yml config.
  3. Change to the actual hook you want (e.g. on_right_click).
  4. Add a log message first, before animations or effects.
  5. Add one real effect (animation, sound, particle).
  6. Only after that, add helpers, state, scheduler logic, or zones.

That order makes debugging dramatically easier because only one thing changes at a time.


既成スクリプト

FMM ships with four premade Lua scripts:

  • invulnerable.lua -- Cancels left-click damage events, making the prop indestructible. This is the simplest useful prop script.
  • pickupable.lua -- Lets players pick up a prop by hitting it three times. Each hit plays a hurt animation on the prop, and on the third hit the prop is removed and drops its placement item for the player to collect.
  • storage_double.lua -- Turns a prop into a double chest (54 slots). Right-click opens a persistent inventory GUI. Plays open/close animations and sounds. Contents are saved to the prop and survive server restarts. On destroy, all contents are dropped.
  • storage_single.lua -- Same as storage_double but with 3 rows (27 slots) instead of 6.

You can find more examples on the Examples & Patterns page.


Luaサンドボックス

Prop and item scripts run inside the same sandboxed LuaJ environment as EliteMobs. The sandbox restrictions are identical. For the full list of removed globals and available standard library functions, see the MagmaCore Lua Scripting Engine page.


次のステップ

  • Prop & Item API -- full context.prop, context.item, context.event, context.world, context.zones, and context.scheduler reference
  • Examples & Patterns -- complete working scripts for props and items with walkthroughs
  • Troubleshooting -- common issues, debugging tips, and a QC checklist

If you are also writing EliteMobs Lua powers, the shared APIs (context.world, context.zones, context.scheduler, context.state, context.log) work identically. See the EliteMobs Lua documentation for the boss-specific APIs.