Skip to main content

Authored enchantments

EliteMobs and FreeMinecraftModels supply custom enchantments through a shared MagmaCore system. Each plugin owns its definitions and Lua effects under its own enchantments/ folder. Native Minecraft enchantments remain in the minecraft: namespace.

Updating bundled definitions​

The catalog restores missing bundled YAML and Lua files and adds missing default fields to existing bundled definitions. It preserves values already configured and existing Lua source. This works even when the enchantments/ directory already exists.

The owning plugin may first archive files that match a retired format. For example, EliteMobs preserves enchantment YAML containing maxLevelV2 before generating current defaults. EliteMobs also archives unedited copies of earlier bundled enchantment scripts, such as an old grappling_hook.lua, so the current default is written in their place; a script you edited stays. Review custom settings in the configuration archive and reapply them using the current fields. An updated DLC or custom effect still needs its own matching definition and Lua source.

Item references​

Custom effects use a lowercase namespaced ID and a positive integer level. This example is an FMM item definition:

enchantments:
- "minecraft:unbreaking,3"
- "elitemobs:critical_strikes,2"

For example, critical_strikes.yml in EliteMobs' enchantment catalog defines elitemobs:critical_strikes. An FMM definition uses freeminecraftmodels:. Subfolders do not become part of the ID; give content files unique filenames.

EliteMobs custom-item YAML retains Bukkit names such as UNBREAKING,3 for native enchantments, while requiring namespaced IDs for custom effects. Do not copy FMM's native-item syntax into another plugin without checking that plugin's item format.

The installed provider must recognize the ID. Compatibility, item type, slot, conflicts, and required capabilities come from its definition. Removing a provider does not turn its custom effect into a native enchantment.

Definition format​

This is the shipped FMM Ignition definition, paired with ignition.lua in the same enchantment catalog:

isEnabled: true
name: Ignition
description: Sets surviving targets on fire after a staff fireball hits.
maxLevel: 3
curse: false
validSlots: [MAINHAND]
itemTypes: [STAFF]
attackKinds: [STAFF_FIREBALL]
requires: []
conflicts: []
stacking: source_item
script: ignition.lua
parameters:
base_ticks: 20
ticks_per_level: 20
return {
api_version = 1,
on_ignition_ticks = function(context)
return math.max(0, math.min(200, context.parameters.base_ticks
+ context.parameters.ticks_per_level * context.enchantment.level))
end
}

This query returns an ignition duration to FMM. It does not run as a prop script or an equipped-item script.

FieldMeaning
isEnabledDefaults to true; disabled definitions do not enter the catalog
nameRequired plain display name
descriptionOptional description, empty by default
maxLevelPositive enchanting level cap; explicitly authored items may exceed it
curseWhether the enchantment is a curse; defaults to false
validSlotsEquipment slots: HEAD, CHEST, LEGS, FEET, MAINHAND, OFFHAND
itemTypesAccepted categories: SWORD, AXE, PICKAXE, SHOVEL, HOE, BOW, CROSSBOW, TRIDENT, MACE, SPEAR, HELMET, CHESTPLATE, LEGGINGS, BOOTS, SHIELD, FISHING_ROD, SHEARS, ELYTRA, WAND, STAFF, OTHER
attackKindsOptional magic attack filter: WAND_MISSILE, STAFF_MELEE, STAFF_FIREBALL
requiresProvider capability requirements
conflictsOther lowercase namespaced enchantment IDs
stackingsource_item by default, or sum_equipped_levels
scriptRequired lowercase .lua filename without a directory
parametersNamed scalar values or per-level scalar lists

A per-level list must have exactly maxLevel entries. Above that level, the last list value is retained while the script still receives the actual enchantment level. Parameters belong to the particular effect; putting an arbitrary key here does not create a new mechanic.

Unknown top-level fields, duplicate IDs, missing scripts, and unsupported hooks reject the candidate catalog. Fix the file named in the error before reloading. A failed candidate is not published over the active catalog.

Effect ownership​

Start from an effect shipped by the plugin that will own your new definition. Its supported hooks and domain operations determine what it can do. Query hooks return values such as missile count, critical chance, or threat bonus. Action hooks handle accepted item inputs and can own timed effects.

The old FMM context.item API and its equip/unequip lifecycle do not apply here. Enchantment scripts use their own context.enchantment, context.parameters, captured source information, and action operations. Do not rename an old item script and assume its context is compatible.

Books and anvils​

Custom enchanted books use the same provider definitions as equipped items. An anvil validates the source item, book, and complete result, including native enchantments. Equal custom levels combine to the next level; unequal levels keep the higher one. Each changed custom enchantment adds its resulting level to the native repair cost. The resulting level must pass the definition's anvil level limit, slot, type, capability, and conflict checks.

Mixed books must transfer every native entry accepted by the server as well as their custom entries. If the server rejects a native entry, the custom operation is rejected instead of silently discarding that part of the book. Invalid or unavailable definitions also reject the preview. The inputs and provider state are checked again at pickup; a visible preview is not a reservation that survives a catalog change.

Lua query reference​

Numeric queries run a fresh script instance and close it immediately after the result. Return one finite number or nil for no contribution. State and scheduled work do not survive the query. A failed definition is quarantined until a successful catalog reload; exhausting the caller's remaining budget does not itself quarantine the definition.

ContextContents
context.enchantmentid and actual level
context.parametersThis definition's values resolved for that level
context.inputScalar facts supplied by the owning mechanic; use the shipped effect for the exact keys
context.targetTarget Entity or Living Entity table, or nil
context.playerTriggering player when the query has one

FMM supplies on_missile_count, on_missile_damage_factor, on_blast_radius_factor, and on_ignition_ticks. Other providers expose their own numeric hooks. A definition may only declare hooks accepted by its provider.

Lua action reference​

Action scripts use the shared scheduler, cooldowns, world, zones, player, and event tables. They run for one accepted input or attack, using a captured source for subsequent callbacks. Chat snapshots the message during the chat event, then captures equipment when dispatch runs on the server thread. An optional parameters.lifetime_ticks must be a positive whole number; without it the activation has a one-tick lifetime. Scheduled effects need an authored lifetime long enough to finish.

Activations stop when their lifetime ends, the actor dies, disconnects or changes world, the owning world unloads, a chunk containing one of their owned entities unloads, or the provider reloads or disables. They can finish earlier once the final input has no remaining owned work. parameters.stop_on_unequip: true also stops an activation when its captured item is no longer equipped. Local cooldowns belong to the player and enchantment ID; global cooldowns belong to the player within the provider. Both survive equipment swaps, completed activations, world changes, and valid reloads, and clear on disconnect or provider shutdown.

HookAccepted input
on_attack_entitySuccessful, positive native melee damage with the main-hand item, or an explicit plugin-owned attack
on_projectile_hitSuccessful living-target damage from a captured bow projectile, delivered once per projectile and target, or an explicit plugin-owned impact
on_take_damagePositive damage to the player; eligible equipped slots are inspected
on_right_click, on_left_clickHand interaction while not sneaking
on_shift_right_click, on_shift_left_clickHand interaction while sneaking; these replace the corresponding ordinary click hook
on_break_blockAccepted player block break using the main-hand item
on_chatAccepted chat, dispatched on the server thread with the message snapshot
on_sneakEntering sneak; equipment contributions are collected per definition
on_projectile_launchConsumed enchanted ammunition launching as an arrow from a bow event
on_game_tickTick while this activation remains active
on_zone_enter, on_zone_leavePlayers crossing a zone watched by this activation

These hooks do not create an equip/unequip script runtime. A tick-only definition still needs an input hook to start its activation. Native attack, damage, chat, and mining hooks observe already accepted inputs; they cannot cancel the original event. The four interaction hooks expose a synchronous cancellation flag: context.event:cancel() denies the associated item and block interaction. Scheduled callbacks cannot retroactively cancel an input.

ContextContents
context.enchantmentid, level
context.parametersLevel-resolved definition parameters
context.playerCurrent actor Player Table
context.targetCurrent affected Living Entity Table, or nil
context.sourceCaptured facts: item, slot, inventory_slot, equipment, attack_kind, providers; some inputs also supply ammunition, projectile, message, block, drop_items, sneaking, or equipped_levels
context.itemCaptured-slot durability and consumption methods, when an inventory item remains addressable; see item methods
context.actionid (attack UUID), instance (activation token), actor (UUID), world (world UUID), and methods below

Item snapshots contain material, amount, and enchantments. They are data, not live inventory objects. UUIDs in source facts are strings. Mining's source.block contains x, y, z, and lowercase material.

Action methodReturnsBehavior
stop()nilStop this activation and clean up its owned work
has_previous()boolTest for another active instance of this enchantment for the same player
replace_previous()nilStop those earlier instances, preserving this one
damage_target(target_uuid, amount)boolAsk the provider to apply finite positive damage with the captured source attribution; false when rejected
own_entity(uuid)boolRegister a valid same-world nonplayer entity for removal at cleanup; use after spawning it
launch_projectile(location, direction, speed, range, ticks, impact, visual?, protected_path?=false)boolLaunch an owned scripted projectile; speed/range must be finite and positive and ticks at least 1
attribute_projectile(uuid, callback, protect?=false)boolBind an owned native projectile's damage to an authored callback
place_block(x, y, z, expected_material, block_data)boolPlace through player block-action validation only if the current material matches
break_block(x, y, z, expected_material, drop_material?, amount?=1)boolBreak through player block-action validation with an optional authored item drop
break_naturally(x, y, z, expected_material)boolBreak using the captured tool and the source break event's drop policy
temporary_block(x, y, z, block_data, ticks?=0)boolLease a loaded block, restoring it at cleanup or after positive ticks; zero lasts until cleanup
can_own_block(x, y, z)boolCheck loaded coordinates and whether temporary ownership is available
temporary_gravity(uuid, enabled)boolLease gravity state and restore it at cleanup
restore_gravity(uuid)boolRestore this activation's gravity lease early
temporary_scale(uuid, scale)boolLease a living entity's scale until cleanup
temporary_potion(uuid, type, ticks, amplifier)boolLease a potion effect for positive ticks and a nonnegative amplifier
restore_potion(uuid, type)boolRestore this activation's potion lease early

Projectile impact callbacks receive (fresh_context, target_or_nil, location); visual callbacks receive (fresh_context, location). Native projectile damage callbacks receive (fresh_context, target, native_damage) on the next tick. They replace the native application and must explicitly apply the intended damage, normally through fresh_context.action:damage_target(target.uuid, amount). With native protect=true, protected explosion blocks and ignition are filtered. Scripted protected_path=true absorbs flights crossing protected or unloaded positions.

Owned cleanup is limited to operations that register ownership. Ordinary world:spawn_entity, entity:set_gravity, and world:set_block_at do not automatically gain action cleanup. Temporary state methods manage restoration; they are not substitutes for checking whether a hostile effect or block edit is permitted.

Upgrading existing content​

FMM skips custom items with nonempty scripts: or retired unnamespaced enchantment entries. Replace those item definitions with namespaced enchantments; prop scripts remain supported.

EliteMobs custom effects now use maxLevel and effect-specific parameters in provider YAML. Native and elite-level enchantment configuration is a separate format. Follow the EliteMobs enchantment reference rather than copying an old maxLevelV2 file into a provider definition.