Wizards of LuaСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачатьСкачать
ВЕРСИИ (168)
release
Wizards of Lua 7.0.0-26.3
7.0.0-26.315 окт. 2026 г.138Minecraft: 26.3fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-26.2
7.0.0-26.215 окт. 2026 г.28Minecraft: 26.2fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-26.1.2
7.0.0-26.1.215 окт. 2026 г.26Minecraft: 26.1.2fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-26.1.1
7.0.0-26.1.115 окт. 2026 г.10Minecraft: 26.1.1fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.11
7.0.0-1.21.1115 окт. 2026 г.60Minecraft: 1.21.11fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.10
7.0.0-1.21.1015 окт. 2026 г.4Minecraft: 1.21.10fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.9
7.0.0-1.21.915 окт. 2026 г.4Minecraft: 1.21.9fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.8
7.0.0-1.21.815 окт. 2026 г.6Minecraft: 1.21.8fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.7
7.0.0-1.21.715 окт. 2026 г.6Minecraft: 1.21.7fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.6
7.0.0-1.21.615 окт. 2026 г.2Minecraft: 1.21.6fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.5
7.0.0-1.21.515 окт. 2026 г.8Minecraft: 1.21.5fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.4
7.0.0-1.21.415 окт. 2026 г.8Minecraft: 1.21.4fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.3
7.0.0-1.21.315 окт. 2026 г.4Minecraft: 1.21.3fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.2
7.0.0-1.21.215 окт. 2026 г.6Minecraft: 1.21.2fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 7.0.0-1.21.1
7.0.0-1.21.115 окт. 2026 г.24Minecraft: 1.21.1fabric
Что нового
- **Breaking Change: Structures save and load themselves: `structure:save` and `Structure:load`**
- They replace `Server:saveStructure` and `Server:loadStructure`. The returned path has "/" between the folders on every system.
```lua
struct:save("mickkay:tower") -- before: spell.server:saveStructure("mickkay:tower", struct)
local loaded = Structure:load("mickkay:tower") -- before: spell.server:loadStructure("mickkay:tower")
```
- **Breaking Change: A skin is a `Skin` object**
- `player.skin` is the `Skin` a player wears (`nil` for a default skin), no longer an account name. Assign a `Skin` to put it on, `nil` for a default skin. `Player:setSkin` and `Player:getSkin` are gone.
- `Skin:lookup(account)` gets an account's skin from Mojang; `skin:save(path)` and `Skin:load(path)` keep it in the world folder.
```lua
local skin = Skin:lookup("jeb_") -- before: player:setSkin("jeb_")
if skin then player.skin = skin end
```
- **Breaking Change: `MobEntity.lookAt` is now `MobEntity.watchTarget`**
- `lookAt` is now a function that turns any entity once, see below.
- **Breaking Change: A mob's attack task counts `task.hits` and `task.shots` apart**
- `task.hits` counts the hits that hurt the target, `task.shots` the shots fired. `task.hits` counted both.
- **Breaking Change: `ChatMessageEvent` also fires for `/say` and `/me` of anyone**
- `evt.sender` is the `Entity` or `Spell` that sent the message (`nil` for a command block or the console), not always a player. New: `evt.senderName` and `evt.kind` ("chat", "say" or "emote").
- **New: Bot players: players without a client that spells control**
- `Bot:spawn(name, pos)` makes a bot that joins like a player, so every event fires as for a player. Use bots to play-test a mini-game, or as companions.
- A bot acts like a player at the keyboard. Tasks let it walk along paths it finds, follow, flee, fight, stay, break and place blocks, and fill or empty containers. `BotMessageEvent` and `BotSoundEvent` tell what it sees and hears. See [Bot](https://wizards-of-lua.net/api/Bot/).
- **New: The Squire example**
- A bot that follows you, fights at your side and holds a spot: `/squire hire <name>`.
- **New: `Entity:lookAt(target)` turns any entity once to a position or another entity**
- `spell:lookAt` takes an entity too.
- **New: `Player.flying` tells and sets whether a player flies**
- **New: An interceptor doesn't get the events it causes itself**
- So a callback that answers each chat message in the chat doesn't call itself without end.
- **Fix: The Journey example works on Minecraft 1.21.5, and `/journey cancel` says when there is no journey to cancel**
- **Fix: An invalid value for `sneaking` names `sneaking` in the error message**
release
Wizards of Lua 6.3.0-26.3
6.3.0-26.311 окт. 2026 г.50Minecraft: 26.3fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-26.2
6.3.0-26.211 окт. 2026 г.16Minecraft: 26.2fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-26.1.2
6.3.0-26.1.211 окт. 2026 г.8Minecraft: 26.1.2fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-26.1.1
6.3.0-26.1.111 окт. 2026 г.10Minecraft: 26.1.1fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.11
6.3.0-1.21.1111 окт. 2026 г.40Minecraft: 1.21.11fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.10
6.3.0-1.21.1011 окт. 2026 г.0Minecraft: 1.21.10fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.9
6.3.0-1.21.911 окт. 2026 г.0Minecraft: 1.21.9fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.8
6.3.0-1.21.811 окт. 2026 г.4Minecraft: 1.21.8fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.7
6.3.0-1.21.711 окт. 2026 г.2Minecraft: 1.21.7fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.6
6.3.0-1.21.611 окт. 2026 г.0Minecraft: 1.21.6fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.5
6.3.0-1.21.511 окт. 2026 г.8Minecraft: 1.21.5fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.4
6.3.0-1.21.411 окт. 2026 г.6Minecraft: 1.21.4fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.3
6.3.0-1.21.311 окт. 2026 г.2Minecraft: 1.21.3fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.2
6.3.0-1.21.211 окт. 2026 г.2Minecraft: 1.21.2fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.3.0-1.21.1
6.3.0-1.21.111 окт. 2026 г.18Minecraft: 1.21.1fabric
Что нового
- **New: Ask whether an entity sees another one: `LivingEntity:canSee`**
- `entity:canSee(other)` tells whether no block is in the line of sight from the entity's eyes to the other entity's eyes, the way the game decides whether a mob sees its target.
- Only blocks are in the way, not other entities, and which way the entity looks doesn't count: it also "sees" what is behind it.
- It is always `false` for an entity in another world, or more than 128 blocks away.
- `entity:canSee(other, range)` shortens how far the entity sees, in blocks from eyes to eyes: a short-sighted guard, or a mob that notices a player only close by. 128 blocks remain the most: a larger `range` changes nothing.
```lua
local zombie = spell:summon("zombie") --[[@as LivingEntity]]
if zombie:canSee(spell.owner) then
print("The zombie sees you.")
end
if zombie:canSee(spell.owner, 8) then
print("The zombie sees you from close by.")
end
```
- **New: Read and set a player's hunger: `Player.foodLevel` and `Player.saturation`**
- `player.foodLevel` is the food level, an integer from 0 to 20. See [Hunger](https://minecraft.wiki/w/Food#Hunger).
- `player.saturation` is the saturation, from 0 up to the food level: a larger value is cut down to it. See [Saturation](https://minecraft.wiki/w/Food#Saturation).
- Both can be assigned, for example to start a round with every player fed.
```lua
for _, player in ipairs(spell:findEntities("@a")) do
---@cast player Player
player.foodLevel = 20
player.saturation = 5
end
```
- **New: Read the game's difficulty: `Server.difficulty`**
- `spell.server.difficulty` is "peaceful", "easy", "normal" or "hard". All worlds of the server share it, and a hardcore world is always "hard".
- It is read-only: change it with the command `/difficulty`.
```lua
if spell.server.difficulty == "hard" then
print("Skeletons shoot twice as often.")
end
```
- **New: Turn the spell to a position: `Spell:lookAt`**
- `spell:lookAt(pos)` sets the spell's `yaw` and `pitch` so that it looks at the position. A raycast from the spell then leads there.
- Looking straight up or down, the spell keeps its yaw.
```lua
spell:lookAt(spell.owner.eyePos)
local hit = spell:raycastBlock(20)
print(hit and "A block is in the way." or "Nothing between us.")
```
- **Change: A mob that follows or chases searches wider before it gives up at a dead end**
- A mob with `startFollow`, or one that chases with `startAttack`, walked to where its path ended, at the foot of a cliff or at a wall, and its task failed there with "unreachable", although a way led around: the way was longer than `searchRange`.
- Now the mob searches again from such a dead end, up to 4 times as far as `searchRange`. Only when that search finds no way either, the task fails with "unreachable", as before.
- A mob that used to stop at a wall with a long way around it now takes that way.
- **Fix: `PlayerInventory` accepts additional properties, like every other object**
- `player.inventory.myNote = "..."` raised "attempt to modify unknown table index". Now a spell can keep its own values on an inventory, as it can on a player or an item.
- **Fix: `LivingEntity.hostile` is `true` for every enemy mob**
- It was `false` for the slime, magma cube, ghast, phantom, shulker, hoglin and ender dragon. A spell that picks its targets by `hostile` now finds these seven, too.
- Nothing else changes: neutral mobs like the wolf, the bee and the iron golem are still not hostile, and the enderman, the piglin and the zombified piglin still are.
release
Wizards of Lua 6.2.0-26.3
6.2.0-26.37 окт. 2026 г.51Minecraft: 26.3fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-26.2
6.2.0-26.27 окт. 2026 г.16Minecraft: 26.2fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-26.1.2
6.2.0-26.1.27 окт. 2026 г.10Minecraft: 26.1.2fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-26.1.1
6.2.0-26.1.17 окт. 2026 г.1Minecraft: 26.1.1fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.11
6.2.0-1.21.117 окт. 2026 г.23Minecraft: 1.21.11fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.10
6.2.0-1.21.107 окт. 2026 г.3Minecraft: 1.21.10fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.9
6.2.0-1.21.97 окт. 2026 г.0Minecraft: 1.21.9fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.8
6.2.0-1.21.87 окт. 2026 г.2Minecraft: 1.21.8fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.7
6.2.0-1.21.77 окт. 2026 г.6Minecraft: 1.21.7fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.6
6.2.0-1.21.67 окт. 2026 г.2Minecraft: 1.21.6fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.5
6.2.0-1.21.57 окт. 2026 г.6Minecraft: 1.21.5fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.4
6.2.0-1.21.47 окт. 2026 г.9Minecraft: 1.21.4fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.3
6.2.0-1.21.37 окт. 2026 г.4Minecraft: 1.21.3fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.2
6.2.0-1.21.27 окт. 2026 г.6Minecraft: 1.21.2fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.2.0-1.21.1
6.2.0-1.21.17 окт. 2026 г.16Minecraft: 1.21.1fabric
Что нового
- **New: Hide an entity from single players: `Entity:setHiddenFrom` and `isHiddenFrom`**
- `entity:setHiddenFrom(player, hidden)` hides the entity from one player, or from a list of players, and `entity:isHiddenFrom(player)` tells whether it is hidden from that player.
- A player the entity is hidden from is not told about it at all: they don't see it, its equipment or its name, and they can neither hit it nor click it nor be pushed by it. All other players see it as before.
- The entity itself is not changed. It still acts on the player it is hidden from, so a hidden zombie still hurts them, and that player still hears its sounds and sees what it shoots. A hidden player keeps their entry in the player list.
- This is independent of `invisible` and of `LivingEntity:setHidden`, which hide something from all players alike.
- The settings are not saved. A hidden player keeps them through death and respawn, and loses them at logout. To hide an entity from the players who join later, hide it from each of them at their `PlayerJoinedEvent`.
```lua
-- A marker that only the owner of this spell sees
local marker = spell:summon("armor_stand")
local owner = spell.owner
for _, player in ipairs(spell:findEntities("@a")) do
if player ~= owner then
marker:setHiddenFrom(player, true)
end
end
```
- **New: A mob can hold a spot: `MobEntity:startStay` with `pos` and `radius`**
- With `pos`, the mob walks to that position first and stays there. With `radius`, it may be moved that many blocks off its spot; when a hit, a push or a current moves it further, it walks back.
- Without `pos`, its spot is the place where it stands when the task starts. Without both options `startStay` works as before: the mob stays where it ends up.
- `duration` counts from when the mob first stands on its spot. `searchRange` and `speed` apply to the way there.
- While the mob holds its spot, `task` also carries `pos`, `radius` and `away`, which is `true` while it is on its way to the spot.
```lua
guard:startStay({ pos = post, speed = 1.3 })
other:startStay({ radius = 3 })
```
- **Fix: `str` and `inspect` show the fields of an object again**
- For a `Block`, a `Vec3` and every other instance of a class they printed only e.g. `<Block 1>`. They show the fields again, as before 6.0.0.
- **Fix: `PlayerHandSwingEvent` fired twice for a right click at a block on Minecraft 26.3**
- Placing a block, pulling a lever or opening a chest fired the event twice. It fires once now, as on the other versions.
release
Wizards of Lua 6.1.0-26.3
6.1.0-26.35 окт. 2026 г.42Minecraft: 26.3fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-26.2
6.1.0-26.25 окт. 2026 г.9Minecraft: 26.2fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-26.1.2
6.1.0-26.1.25 окт. 2026 г.6Minecraft: 26.1.2fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-26.1.1
6.1.0-26.1.15 окт. 2026 г.5Minecraft: 26.1.1fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.11
6.1.0-1.21.115 окт. 2026 г.17Minecraft: 1.21.11fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.10
6.1.0-1.21.105 окт. 2026 г.0Minecraft: 1.21.10fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.9
6.1.0-1.21.95 окт. 2026 г.3Minecraft: 1.21.9fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.8
6.1.0-1.21.85 окт. 2026 г.2Minecraft: 1.21.8fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.7
6.1.0-1.21.75 окт. 2026 г.2Minecraft: 1.21.7fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.6
6.1.0-1.21.65 окт. 2026 г.1Minecraft: 1.21.6fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.5
6.1.0-1.21.55 окт. 2026 г.2Minecraft: 1.21.5fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.4
6.1.0-1.21.45 окт. 2026 г.2Minecraft: 1.21.4fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.3
6.1.0-1.21.35 окт. 2026 г.2Minecraft: 1.21.3fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.2
6.1.0-1.21.25 окт. 2026 г.5Minecraft: 1.21.2fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.1.0-1.21.1
6.1.0-1.21.15 окт. 2026 г.6Minecraft: 1.21.1fabric
Что нового
- **New: A mob can search a path before it walks it: `MobEntity:findPath`, `MobPath` and `MobEntity:startPathAlong`**
- `mob:findPath(target, options)` searches the path `startPathTo` would start on and returns it as a `MobPath`, without moving the mob. It returns `nil` if there is no path at all.
- A `MobPath` has the read-only fields `length`, `nodes`, `reachable` and `target`. So a spell can compare the ways to two targets, or see where a way leads, before the mob takes a step.
- Given a list of positions, `findPath` searches a path that comes by each of them in order: a patrol, or a way around a place the mob should avoid.
- `mob:startPathAlong(path, options)` starts a task that moves the mob along that path and no other. It doesn't search again: if the mob is held up, the task fails with `"unreachable"`. It doesn't start if the mob isn't on the path (`"off_path"`).
```lua
local a, b = villager:findPath(well), villager:findPath(farm)
local path = a
if not (a and a.reachable) or (b and b.reachable and b.length < a.length) then
path = b
end
if path and path.reachable then
villager:startPathAlong(path)
villager:awaitTask()
end
```
- **New: Hide aspects of a living entity from the players: `LivingEntity:setHidden` and `isHidden`**
- `entity:setHidden(aspect, hidden)` hides one aspect, or a list of them, and `entity:isHidden(aspect)` tells whether one is hidden.
- The aspects are `"equipment"` (what the entity holds and wears), `"arrows"` (the arrows and bee stingers that stick in it), `"effectParticles"`, `"stepParticles"` (the dust of sprinting and landing) and `"stepSounds"`.
- Together with `invisible`, which hides the body only, nothing gives an entity away any more.
- The entity itself is not changed: it keeps its items and its effects. The settings are not saved. A player keeps them through death and respawn, and loses them at logout.
```lua
local player = spell.owner
player.invisible = true
player:setHidden({ "equipment", "arrows", "effectParticles", "stepParticles", "stepSounds" }, true)
```
- **New: A player can wear another player's skin: `Player:setSkin` and `Player.skin`**
- `player:setSkin(name, timeout)` puts on the skin of the player account with that name, also in the middle of a game. The player keeps their own name and UUID. All players see the new skin at once.
- The skin is looked up at Mojang, which needs internet access. The function waits for it, usually less than a second, and returns `true`, or `false` and the reason: `"not_found"`, `"unavailable"`, `"offline"`, `"replaced"` or `"timeout"`.
- `player:setSkin(nil)` gives the player their own skin back. The read-only `player.skin` is the name of the player whose skin they wear.
- The skin stays on through death and respawn, and is gone when the player logs out.
```lua
local ok, reason = spell.owner:setSkin("jeb_")
if not ok then
print("No skin: " .. reason)
end
```
- **New: `Player.online`**
- The read-only `player.online` tells whether the player is still on the server. It is `false` once the player has left or was kicked; a dead player is still online.
- **Change: A reference to a player follows the player through respawn**
- A variable that holds a player, and `spell.owner`, now refer to the respawned player after a death. Before, they kept referring to the dead one, whose position and health no longer changed.
- In an `AfterPlayerRespawnEvent`, `spell.owner` therefore equals `event.newPlayer`, no longer `event.oldPlayer`.
- A reference doesn't follow a player through logout: a player who logs in again is a new player, and an old reference stays offline, as `player.online` tells.
- **Change: `MobEntity:startPathTo` reaches targets beyond its search range**
- A path search looks `searchRange` blocks ahead, so a path to a distant target, or along a long, winding way, may end short of it. The mob now searches again from where such a path ends, as long as that gets it nearer the target. Before, the mob went no further than the end of that first path.
- `task.reachable` is `false` while the current path stops short of the target; the mob may still get there.
release
Wizards of Lua 6.0.1-26.3
6.0.1-26.33 окт. 2026 г.30Minecraft: 26.3fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-26.2
6.0.1-26.23 окт. 2026 г.13Minecraft: 26.2fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-26.1.2
6.0.1-26.1.23 окт. 2026 г.3Minecraft: 26.1.2fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-26.1.1
6.0.1-26.1.13 окт. 2026 г.0Minecraft: 26.1.1fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.11
6.0.1-1.21.113 окт. 2026 г.27Minecraft: 1.21.11fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.10
6.0.1-1.21.103 окт. 2026 г.6Minecraft: 1.21.10fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.9
6.0.1-1.21.93 окт. 2026 г.2Minecraft: 1.21.9fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.8
6.0.1-1.21.83 окт. 2026 г.4Minecraft: 1.21.8fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.7
6.0.1-1.21.73 окт. 2026 г.1Minecraft: 1.21.7fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.6
6.0.1-1.21.63 окт. 2026 г.2Minecraft: 1.21.6fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.5
6.0.1-1.21.53 окт. 2026 г.3Minecraft: 1.21.5fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.4
6.0.1-1.21.43 окт. 2026 г.1Minecraft: 1.21.4fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.3
6.0.1-1.21.33 окт. 2026 г.5Minecraft: 1.21.3fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.2
6.0.1-1.21.23 окт. 2026 г.5Minecraft: 1.21.2fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.1-1.21.1
6.0.1-1.21.13 окт. 2026 г.11Minecraft: 1.21.1fabric
Что нового
- **Change: The `io` library follows Lua 5.3** (rembulan [#15](https://github.com/wizards-of-lua/rembulan/issues/15) to [#26](https://github.com/wizards-of-lua/rembulan/issues/26))
- Wizards of Lua now uses **rembulan 0.5.0**, whose `io` library behaves like the one of Lua 5.3.
- `io.lines(filename)` is available.
- `read` accepts the format names without the asterisk (`"a"`, `"l"`, `"L"`, `"n"`) as well as with it, `read("a")` at the end of a file returns `""` rather than `nil`, and `read(0)` no longer consumes a byte.
- A failed read returns `nil` and a message instead of raising an error, and `read` with several formats stops at the first one that fails.
- `io.output()` returns the default output file. Before, it returned the default input file.
- Closing a file that is already closed raises an error, and `tostring` of a closed file gives `"file (closed)"`.
- The files a spell left open are closed when the spell ends.
- `io.tmpfile` and `io.popen` remain unavailable: a spell may only use files inside the world folder.
- **Fix: `string.format` writes numbers like Lua does, on every server** (rembulan [#27](https://github.com/wizards-of-lua/rembulan/issues/27))
- `%f`, `%e` and `%g` used the server's language settings, so a server set to German wrote `1,5` instead of `1.5`, which broke generated JSON and commands, for example.
- `%g` no longer keeps trailing zeros (`string.format("%.14g", 1)` is `"1"`), a precision of 0 is respected, and numbers are rounded as in Lua.
- **Fix: `]` and `-` as literal characters in pattern sets** (rembulan [#28](https://github.com/wizards-of-lua/rembulan/issues/28))
- A `]` right after the opening bracket, as in `[]]` or `[^]]`, and a `-` right before the closing bracket, as in `[a-]`, are now taken literally, as in Lua.
- **Fix: Large Lua sources can be loaded** (rembulan [#29](https://github.com/wizards-of-lua/rembulan/issues/29), [#30](https://github.com/wizards-of-lua/rembulan/issues/30), [#31](https://github.com/wizards-of-lua/rembulan/issues/31))
- `load` failed for a source of more than 64 KB.
- A chunk with a large table constructor, e.g. a data file with thousands of records, took very long to compile and then failed with "Method too large". Such chunks now compile, and much faster: 20,000 records take about a second instead of half a minute.
- A very large function could run in an endless loop instead of finishing.
- **Fix: Multiple assignment to properties** (rembulan [#32](https://github.com/wizards-of-lua/rembulan/issues/32))
- In an assignment like `spell.yaw, spell.pitch = f()`, where one function call provides several values, only the first target got its value when the targets were properties of an object, and the others raised an error such as "bad argument to 'pitch'". Plain tables and local variables were not affected.
- **Fix: `mob.lookAt` accepts a position**
- `mob.lookAt = Vec3(x, y, z)` raised an error; only an entity worked. Now a `Vec3` or a plain table like `{ x = 1, y = 2, z = 3 }` works as documented.
- **Fix: Block tasks reach blocks they failed to reach**
- `startBreakBlock`, `startPutInto` and `startTakeFrom` failed with "unreachable" when the mob stood on top of the block, e.g. on a chest: its eyes were a little too far from the block's middle for the default `reach`. A mob that stands on the block or right next to it now always reaches it.
- A task on a block high above the ground, e.g. the top barrel of a stack of three, failed with "unreachable" when something else stood nearby that the mob could climb. The mob now walks to a place from which the block is within its `reach`. This applies to `startPlaceBlock` too.
- **Fix: Documentation of `PlayerHandSwingEvent.hand`**
- The API documentation now tells which hand the event reports for which action.
release
Wizards of Lua 6.0.0-26.3
6.0.0-26.327 сент. 2026 г.42Minecraft: 26.3fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-26.2
6.0.0-26.227 сент. 2026 г.13Minecraft: 26.2fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-26.1.2
6.0.0-26.1.227 сент. 2026 г.3Minecraft: 26.1.2fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-26.1.1
6.0.0-26.1.127 сент. 2026 г.3Minecraft: 26.1.1fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.11
6.0.0-1.21.1127 сент. 2026 г.31Minecraft: 1.21.11fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.10
6.0.0-1.21.1027 сент. 2026 г.1Minecraft: 1.21.10fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.9
6.0.0-1.21.927 сент. 2026 г.2Minecraft: 1.21.9fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.8
6.0.0-1.21.827 сент. 2026 г.1Minecraft: 1.21.8fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.7
6.0.0-1.21.727 сент. 2026 г.2Minecraft: 1.21.7fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.6
6.0.0-1.21.627 сент. 2026 г.4Minecraft: 1.21.6fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.5
6.0.0-1.21.527 сент. 2026 г.3Minecraft: 1.21.5fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.4
6.0.0-1.21.427 сент. 2026 г.4Minecraft: 1.21.4fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.3
6.0.0-1.21.327 сент. 2026 г.6Minecraft: 1.21.3fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.2
6.0.0-1.21.227 сент. 2026 г.5Minecraft: 1.21.2fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 6.0.0-1.21.1
6.0.0-1.21.127 сент. 2026 г.8Minecraft: 1.21.1fabric
Что нового
- **Breaking Change: `startFollowPathTo` replaced by mob tasks**
- A spell now gives a mob a **task** instead of taking its AI away. While a task runs, the mob's own behavior is suspended; when the task ends, the mob carries on as before — it wanders, panics and defends itself again. Before, the mob was left standing there for good.
- `startFollowPathTo` became `startPathTo`, `followingPath` became `busy`, and `stopFollowPath` became `stopTask`.
- Every `start...` function takes its subject first and everything else in an options table, and returns `true`, or `false` and a reason why the task did not start. A misspelled option raises an error rather than being ignored.
- An unreachable target now fails the task. Before, the mob walked as far as it could get and the call reported success either way.
- **Migration:**
```lua
-- before
pig:startFollowPathTo(waypoint, 0, 100, 2)
while pig.followingPath do sleep(20) end
if (waypoint - pig.pos):magnitude() > 1 then print("did not make it") end
-- after
pig:startPathTo(waypoint, { targetRadius = 0, searchRange = 100, speed = 2 })
while pig.busy do sleep(20) end
if pig.task.state ~= "finished" then print("did not make it: " .. pig.task.reason) end
```
- To keep a mob standing still, use `mob:startStay()` for as long as the spell runs, or `mob.ai = false` to keep it that way for good. The latter is saved with the entity, and such a mob does not even fall.
- **Breaking Change: Hands and hit types are lowercase**
- A hand is now `"mainhand"` or `"offhand"`, as in `getEquipment` and the task options, instead of `"MAIN_HAND"` or `"OFF_HAND"`. This is what the `hand` field of `PlayerAttackBlockEvent`, `PlayerAttackEntityEvent`, `PlayerHandSwingEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent` and `PlayerUseItemEvent` returns.
- `HitResult.type` is now `"block"`, `"entity"` or `"miss"` instead of `"BLOCK"`, `"ENTITY"` or `"MISS"`.
- `getItemInHand` and `setItemInHand` reject the old spelling with an error that names the new one. A comparison with the old spelling, though, is simply no longer true, and no error tells you so.
- **Migration:**
```lua
-- before
if event.hand == "MAIN_HAND" then ... end
if hit.type == "MISS" then ... end
-- after
if event.hand == "mainhand" then ... end
if hit.type == "miss" then ... end
```
- **New: Support for Minecraft 26.1.1, 1.21.2, 1.21.3, 1.21.5, 1.21.6, 1.21.7 and 1.21.9**
- These versions get their first Wizards of Lua release with 6.0.0, so every Minecraft version from 1.21.1 to 26.3 is now supported.
- **New: Mobs can be sent to attack, follow, flee and stay**
- `startAttack(entity, options?)` walks the mob to an entity and attacks it, with the weapon it holds and the damage that weapon gives it. Options: `damage`, `reach`, `cooldown`, `distance`, `chase`, `searchRange`, `speed`, `mode` ("auto", "melee" or "ranged") and `wearTool`. A peaceful mob such as a pig has no attack damage of its own, so it needs `damage`. Like any mob, it does not wear out its weapon, unless `wearTool` is `true`.
- `startFollow(entity, options?)` follows an entity at a distance, `startFleeFrom(entity, options?)` runs away until far enough, and `startStay(options?)` keeps the mob where it is, for a `duration` if one is given.
- `stopTask()` ends the task and hands the mob back to its own behavior.
```lua
local wolf = spell:summon("wolf")
wolf:startFollow(spell.owner, { distance = 3, speed = 1.2 })
wolf.lookAt = spell.owner
```
- **New: `busy` and `task` tell what a mob is doing**
- `busy` is `true` while the mob executes a task. `task` is a copy of the task it is executing, or of the last one it executed: `name`, `state` ("running", "finished", "failed" or "interrupted"), `reason`, `age`, the options it was started with, and for a path how far it has `traveled`, the `remainingDistance` and whether the target is `reachable`.
- Because `task` outlives the task, a failure can be reported with the where and how far, not only that it happened.
- **New: `MobEntity.lookAt`, `ai`, `target` and `canPickUpLoot`**
- `lookAt` is the entity or position a mob keeps looking at, independent of its task, so it can watch you while walking somewhere else.
- `ai` enables or disables the mob's artificial intelligence. It is persisted, and a mob with `ai = false` neither acts, nor executes tasks, nor falls.
- `target` is the entity the mob is attacking, whether a spell told it to or it picked the target itself.
- `canPickUpLoot` tells whether the mob picks up items it walks over.
- **New: Mobs can break and place blocks, and fill and empty containers**
- `startBreakBlock(pos, options?)` walks the mob to a block and breaks it the way a player mines it: the time it takes depends on the tool in its main hand, players see the block crack, and the block drops what it would drop for a player with that tool. The tool wears out unless `wearTool` is `false`.
- `startPlaceBlock(pos, options?)` places the block the mob holds, facing the mob. `against` tells which neighbor it rests against, e.g. a torch on the floor or on a wall; unlike a player, the mob needs no neighbor at all.
- `startPutInto(pos, options?)` puts the items the mob holds into a container, and `startTakeFrom(pos, options?)` takes items out, chosen by an item id, a tag or a list of them. Both follow the rules a hopper follows, so coal goes into a furnace as fuel and raw iron as the item to smelt.
- A mob has only its two hands: the `hand` option tells which one gives or takes the items.
```lua
zombie:setEquipment("mainhand", Item:new("iron_pickaxe"))
zombie:startBreakBlock(spell.pos + Vec3(3, 0, 0))
```
- **New: `awaitTask(timeout?)` waits for a mob's task to end**
- Returns `true` if the task finished, otherwise `false` and the reason, the way the `start...` functions report a task that could not start. So a failed start and a failed task are handled in one place:
```lua
local ok, reason = pig:startPathTo(waypoint)
if ok then
ok, reason = pig:awaitTask()
end
```
- With a `timeout` the wait ends after that many ticks with "timeout"; the task itself keeps running.
- **New: `removeNbt(path, ...)` on `Entity`, `BlockEntity` and `Item`**
- Removes every tag the given NBT paths match and returns how many were removed. Paths use the syntax of the vanilla `/data` command:
```lua
pig:removeNbt("CustomName", 'active_effects[{id:"minecraft:speed"}]')
chest:removeNbt("Items[{Slot:0b}]")
```
- Minecraft only reads some values when they are present, so omitting them does not clear them. `removeNbt` clears the known cases itself (custom name, tags, effects, attributes and their modifiers, health) and raises an error if a removed value is still there afterwards, instead of reporting success.
- **New: `putNbt(path, value)` on `Entity`, `BlockEntity` and `Item`**
- Merges `value` at every tag the NBT path matches, with the same rules as `putNbt(table)`, and returns the number of changed tags. A path can select list elements by content, e.g. `chest:putNbt("Items[{Slot:0b}].count", 16)`.
- In a path, filters must match the stored type exactly (`Slot:0b`, not `Slot:0`) and ids are namespaced. A filter that matches nothing changes nothing.
- **New: `logToConsole(...)` writes to the server console**
- Works like `log(...)`, but always writes to the server console and the server log, whatever `WizardsOfLua.log` is set to. Useful for spells started by the server console, a command block or RCON, where `print(...)` has no one to show its output to.
- **New: `spell:findBlocks(criteria)` and `spell:findBlockEntities(criteria)`**
- Search a sphere around the spell for blocks or block entities, nearest first, filtered by a block id, a tag or a list of them. `findBlocks` returns positions, `findBlockEntities` the block entities:
```lua
local logs = spell:findBlocks({ block = "#minecraft:logs", maxradius = 32, limit = 1 })
local chests = spell:findBlockEntities({ block = { "chest", "barrel" }, maxradius = 16 })
```
- Only loaded chunks are searched.
- **New: Spells can be tagged**
- `spell.tags` is the list of a spell's tags, `spell:addTag(tag)` and `spell:removeTag(tag)` add and remove one. `findSpells({ tag = ... })` finds spells by tag, which it could not before, since no spell could have a tag.
- `/wol spell list byTag <tag>` and `/wol spell break byTag <tag>` list and break the spells with a tag.
```lua
spell:addTag("miner")
for _, s in ipairs(spell:findSpells({ tag = "miner" })) do s:kill() end
```
- **New: `spell.forceChunkRadius` keeps a larger area loaded**
- The number of chunks around the spell's chunk that stay loaded and ticking like the chunk itself. It defaults to 0, the area a spell has always kept loaded.
- **New: Guards example**
- Zombified piglins in iron armor that hold a post and fight off hostile mobs within a radius, without straying further than their leash. Placed with `/guards place <pos> [<radius> <leash>]`. A spell hands the guards their tasks and takes them over again whenever their chunk is loaded, since a mob's task is not saved with it. Installed by `/wol extract examples`; see `examples/guards/Guards.md`.
- **New: Miner example**
- A zombified piglin that digs a tunnel, hangs torches on the wall, and fetches pickaxes and just the torches it needs from the chests near the tunnel entrance, waiting when there are none. Blocks put into the tunnel meanwhile it breaks again. When it is done, it puts its tools back and vanishes. Created with `/miner create miner <pos> <direction> <length>`; see `examples/miner/Miner.md`.
- **New: Named types for the API's values and tables**
- The API files name the values a string accepts, like `Hand` ("mainhand" or "offhand"), `Side`, `EquipmentSlot`, `TaskReason` and `TaskState`, and the tables passed to or returned by functions, like the task options (`StartAttackOptions`, ...), the search criteria and `MobEntity.task` (`MobTask`). Editors with the Lua language server complete these values and fields and flag a wrong or missing one. Run `/wol extract api` again to get the new files.
- The API reference has a new [Types](https://mickkay.codeberg.page/Wizards-of-Lua-Fabric/api/_types/) page that lists them, with a description of every value and field.
- **Change: The undocumented globals `Types` and `Check` are gone**
- `type`, `instanceOf` and `declare` are now global functions of their own, like `log` and `sleep`, instead of wrappers around the `Types` table. `Check`, a leftover nobody used, is gone as well.
- `type`, and the formatting `error(fmt, ...)` and `assert(value, fmt, ...)`, are now documented, so that editors know them too.
- **Change: Assigning `nbt` removes the keys the assigned table omits on every MC version**
- Before, whether an omitted custom name, tag list, effect or attribute was removed depended on the MC version. It is now always removed, so assign a full snapshot (`local nbt = obj.nbt; ...; obj.nbt = nbt`) to keep everything else.
- **Fix: Effects written through NBT did not take effect**
- Adding or removing effects with `putNbt`, `removeNbt` or by assigning `nbt` changed the stored effects, but the client was not told (no icon, particles left over) and the effect's attribute changes were not applied or removed, e.g. Speed did not change the movement speed. Effects now behave as if added or removed with `/effect`.
- **Fix: Writing NBT could change an entity's UUID**
- `putNbt` or assigning `nbt` with a different `UUID` changed the identity of a live entity. The UUID is now kept, as with the vanilla `/data` command; a new one is ignored.
- **Fix: `putNbt()` could not change the NBT type of an existing value**
- Renaming an entity or block entity twice with `putNbt({ CustomName = { text = "..." } })` failed with `Can't convert nbt.CustomName! string expected, but got table` whenever the first name had no formatting, because Minecraft stores an unformatted name as a string and a formatted one as a compound.
- A table is still merged into a stored compound or list. A value of a different kind now replaces the stored value instead of raising an error.
- **Fix: `putNbt()` on `Items`, `Inventory` and `EnderItems` did not update an existing slot**
- Each entry is now merged into the stored entry with the same `Slot`, regardless of its position in the list. Entries for other slots are left alone.
- Partial entries such as `{ Slot = 1, count = 7 }` were silently ignored before and now change only the given fields.
- Because entries are now merged, a new item written into an occupied slot keeps the old item's `components` (e.g. its custom name) unless the entry sets them. To replace a slot completely, assign the full snapshot via `.nbt = ...`.
- **Fix: `putNbt()` on `active_effects` merged effects by list position**
- Effects are now matched by their `id`, so `putNbt({ active_effects = { { id = "speed", amplifier = 3 } } })` changes the stored speed effect and leaves the others alone. Before, it could overwrite whichever effect came first in the list, and a new effect was dropped if the entity already had effects.
- The id may be given with or without the `minecraft:` namespace.
- **Fix: `putNbt()` on `attributes` merged attributes by list position**
- Attributes are now matched by their `id`. Before, `putNbt({ attributes = { { id = "max_health", base = 20 } } })` was merged into whichever attribute came first, so a newly written attribute could inherit that attribute's base value and modifiers.
- **Fix: A spell leaving a chunk released it, even when `/forceload` kept it loaded** ([#41](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/41))
- A spell keeps the chunk it is in loaded. When it moved on, it also undid a `/forceload` of that chunk, so the chunk could unload and its mobs stopped moving. A spell now uses its own chunk ticket and leaves `/forceload` alone.
- The chunks a spell keeps loaded no longer show up in `/forceload query` and `/forceload list`, and are no longer written to the save, so a server crash cannot leave them force-loaded forever.
- **Fix: Clearing table fields while iterating with `pairs` raised an error** ([rembulan#14](https://github.com/wizards-of-lua/rembulan/issues/14))
- `for k in pairs(t) do t[k] = nil end` failed with `invalid key to 'next'`, or with an error showing only the removed key. Lua allows clearing existing fields during a traversal, and this now works.
- **Fix: Players still saw an item taken from a mob in the tick it spawned**
- A zombified piglin summoned by a spell kept showing its golden sword after `setEquipment("mainhand", nil)`, although it no longer held it. `setEquipment` and `setItemInHand` now always tell the players who see the entity.
- **Fix: `findSpells({ owner = ... })` compared with the wrong owner**
- It compared the name with the owner of the spell doing the search, not with the owner of each spell found, so it found every spell with an owner or none at all. A search from a spell without an owner, e.g. one started from the server console, raised an error. It now finds the spells owned by the named entity.
- **Fix: `Server:getWorld()` raised an error for an unknown dimension**
- It failed with `javaObject == null!`. It now returns `nil`.
release
Wizards of Lua 5.0.0-26.3
5.0.0-26.318 сент. 2026 г.52Minecraft: 26.3fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-26.2
5.0.0-26.218 сент. 2026 г.28Minecraft: 26.2fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-26.1.2
5.0.0-26.1.218 сент. 2026 г.21Minecraft: 26.1.2fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-1.21.11
5.0.0-1.21.1118 сент. 2026 г.54Minecraft: 1.21.11fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-1.21.10
5.0.0-1.21.1018 сент. 2026 г.16Minecraft: 1.21.10fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-1.21.8
5.0.0-1.21.818 сент. 2026 г.13Minecraft: 1.21.8fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-1.21.4
5.0.0-1.21.418 сент. 2026 г.16Minecraft: 1.21.4fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 5.0.0-1.21.1
5.0.0-1.21.118 сент. 2026 г.22Minecraft: 1.21.1fabric
Что нового
- **Breaking Change: `PlayerInventory` restructured** ([#40](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/40))
- Item slots moved from the inventory object itself into a new `slots` table: `inventory[i]` is now `inventory.slots[i]` (indices 0–40, copy semantics unchanged, assign `nil` to clear a slot).
- `PlayerInventory.selectedSlot` was renamed to `PlayerInventory.selected`.
- **Migration:**
```lua
-- Before:
local item = player.inventory[20]
player.inventory[20] = item
player.inventory.selectedSlot = 3
-- After:
local item = player.inventory.slots[20]
player.inventory.slots[20] = item
player.inventory.selected = 3
```
- **Breaking Change: `Spell:sleep()` removed** ([#28](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/28))
- Sleeping is only meaningful for the running spell, so the method was removed from `Spell` and can no longer be called on a foreign spell. Use the global `sleep(ticks)` instead, which behaves as before.
- **New: `Server.dedicated`** ([#37](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/37))
- Read-only boolean, `true` on a dedicated server and `false` on an integrated server (singleplayer or LAN).
- **New: Magic Hoe example**
- A chain-harvesting tool that intercepts `BeforePlayerBlockBreakEvent` and flood-fills through a connected wheat field. Installed by `/wol extract examples`; see `examples/magic-hoe/MagicHoe.md`.
- **Change: `/wol extract examples` no longer overwrites existing files**
- The command now refuses to extract into a non-empty target directory instead of silently deleting player-placed files. Use `/wol extract examples overwrite` to force the previous behaviour.
- **Fix: `%v` placeholder in dynamic commands shifted integer X/Z coordinates by +0.5** ([#39](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/39))
- **Fix: `MobEntity:startFollowPathTo(target)` did not move the mob when `targetRadius` was omitted**
- The default `targetRadius` was 50, which made the pathfinder treat any target within 50 blocks as already reached. The default is now 1 (vanilla convention).
- `targetRadius`, `searchRange`, and `speed` are now documented as optional with their defaults (1, 50, 1).
- **Fix: `World.time` stopped advancing when the `advance_time` gamerule was off (MC 26.x only)**
- The 26.x port had mapped `World.time` to the day clock instead of game time. It is now the game-tick counter again, as on the 1.21.x versions. `World.timeOfDay` is unchanged.
release
Wizards of Lua 4.0.4-26.2
4.0.4-26.221 июн. 2026 г.419Minecraft: 26.2fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.4-26.1.2
4.0.4-26.1.221 июн. 2026 г.102Minecraft: 26.1.2fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.4-1.21.11
4.0.4-1.21.1120 июн. 2026 г.180Minecraft: 1.21.11fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.4-1.21.10
4.0.4-1.21.1020 июн. 2026 г.39Minecraft: 1.21.10fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.4-1.21.8
4.0.4-1.21.820 июн. 2026 г.47Minecraft: 1.21.8fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.4-1.21.4
4.0.4-1.21.420 июн. 2026 г.39Minecraft: 1.21.4fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.4-1.21.1
4.0.4-1.21.120 июн. 2026 г.81Minecraft: 1.21.1fabric
Что нового
- **Fix: Cancelling `PlayerDropSelectedItemEvent`, `PlayerUseBlockEvent`, `PlayerUseEntityEvent`, and `PlayerUseItemEvent` causes held item to visually vanish on client** ([#32](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/32))
- **Fix: `sleep()` hangs forever in console/RCON when server is paused** ([#35](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/35))
- **Fix: Setting `Player.gamemode` to `nil` or an invalid string gives cryptic errors** ([#36](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/36))
- Upgraded to **rembulan 0.4.2**
- **Fix: $ end-of-string anchor never matches in any pattern function** ([Rembulan #13](https://github.com/wizards-of-lua/rembulan/issues/13))
release
Wizards of Lua 4.0.3-1.21.11
4.0.3-1.21.1114 апр. 2026 г.184Minecraft: 1.21.11fabric
Что нового
- **Fix: `World.timeOfDay` value must be in range [0, 23999]**
- The property previously returned raw tick values that could exceed 23999; it now applies modulo 24000 to keep the value in range.
- **Fix: `types.getLuaTypeNameOfLuaObject` returned incorrect type names**
- **Fix: `io.open` update modes (`r+`, `w+`, `a+`) not supported** ([#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29))
- **Fix: `math.atan` ignored second argument** ([#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30))
- Per the Lua 5.3 spec, `math.atan(y, x)` should behave like `atan2(y, x)`. The second argument was previously ignored.
- **Fix: Magic characters in `string.match` character groups behaved incorrectly** ([#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
- Upgraded to **rembulan 0.4.1** (fixes [#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29), [#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30), [#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
release
Wizards of Lua 4.0.3-1.21.10
4.0.3-1.21.1014 апр. 2026 г.37Minecraft: 1.21.10fabric
Что нового
- **Fix: `World.timeOfDay` value must be in range [0, 23999]**
- The property previously returned raw tick values that could exceed 23999; it now applies modulo 24000 to keep the value in range.
- **Fix: `types.getLuaTypeNameOfLuaObject` returned incorrect type names**
- **Fix: `io.open` update modes (`r+`, `w+`, `a+`) not supported** ([#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29))
- **Fix: `math.atan` ignored second argument** ([#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30))
- Per the Lua 5.3 spec, `math.atan(y, x)` should behave like `atan2(y, x)`. The second argument was previously ignored.
- **Fix: Magic characters in `string.match` character groups behaved incorrectly** ([#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
- Upgraded to **rembulan 0.4.1** (fixes [#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29), [#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30), [#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
release
Wizards of Lua 4.0.3-1.21.8
4.0.3-1.21.814 апр. 2026 г.42Minecraft: 1.21.8fabric
Что нового
- **Fix: `World.timeOfDay` value must be in range [0, 23999]**
- The property previously returned raw tick values that could exceed 23999; it now applies modulo 24000 to keep the value in range.
- **Fix: `types.getLuaTypeNameOfLuaObject` returned incorrect type names**
- **Fix: `io.open` update modes (`r+`, `w+`, `a+`) not supported** ([#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29))
- **Fix: `math.atan` ignored second argument** ([#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30))
- Per the Lua 5.3 spec, `math.atan(y, x)` should behave like `atan2(y, x)`. The second argument was previously ignored.
- **Fix: Magic characters in `string.match` character groups behaved incorrectly** ([#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
- Upgraded to **rembulan 0.4.1** (fixes [#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29), [#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30), [#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
release
Wizards of Lua 4.0.3-1.21.4
4.0.3-1.21.414 апр. 2026 г.45Minecraft: 1.21.4fabric
Что нового
- **Fix: `World.timeOfDay` value must be in range [0, 23999]**
- The property previously returned raw tick values that could exceed 23999; it now applies modulo 24000 to keep the value in range.
- **Fix: `types.getLuaTypeNameOfLuaObject` returned incorrect type names**
- **Fix: `io.open` update modes (`r+`, `w+`, `a+`) not supported** ([#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29))
- **Fix: `math.atan` ignored second argument** ([#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30))
- Per the Lua 5.3 spec, `math.atan(y, x)` should behave like `atan2(y, x)`. The second argument was previously ignored.
- **Fix: Magic characters in `string.match` character groups behaved incorrectly** ([#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
- Upgraded to **rembulan 0.4.1** (fixes [#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29), [#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30), [#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
release
Wizards of Lua 4.0.3-1.21.1
4.0.3-1.21.114 апр. 2026 г.62Minecraft: 1.21.1fabric
Что нового
- **Fix: `World.timeOfDay` value must be in range [0, 23999]**
- The property previously returned raw tick values that could exceed 23999; it now applies modulo 24000 to keep the value in range.
- **Fix: `types.getLuaTypeNameOfLuaObject` returned incorrect type names**
- **Fix: `io.open` update modes (`r+`, `w+`, `a+`) not supported** ([#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29))
- **Fix: `math.atan` ignored second argument** ([#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30))
- Per the Lua 5.3 spec, `math.atan(y, x)` should behave like `atan2(y, x)`. The second argument was previously ignored.
- **Fix: Magic characters in `string.match` character groups behaved incorrectly** ([#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
- Upgraded to **rembulan 0.4.1** (fixes [#29](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/29), [#30](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/30), [#31](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/31))
release
Wizards of Lua 4.0.2-1.21.11
4.0.2-1.21.115 апр. 2026 г.71Minecraft: 1.21.11fabric
Что нового
- **Fix: `ItemEntity.item` getter returned a live reference instead of a copy** (related to [#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- `ItemEntity.item` now returns a **copy** of the item stack, consistent with all other item getters introduced in 4.0.0.
- Previously, mutating the returned value would silently affect the item entity's actual stack.
release
Wizards of Lua 4.0.2-1.21.10
4.0.2-1.21.105 апр. 2026 г.33Minecraft: 1.21.10fabric
Что нового
- **Fix: `ItemEntity.item` getter returned a live reference instead of a copy** (related to [#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- `ItemEntity.item` now returns a **copy** of the item stack, consistent with all other item getters introduced in 4.0.0.
- Previously, mutating the returned value would silently affect the item entity's actual stack.
release
Wizards of Lua 4.0.2-1.21.8
4.0.2-1.21.85 апр. 2026 г.46Minecraft: 1.21.8fabric
Что нового
- **Fix: `ItemEntity.item` getter returned a live reference instead of a copy** (related to [#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- `ItemEntity.item` now returns a **copy** of the item stack, consistent with all other item getters introduced in 4.0.0.
- Previously, mutating the returned value would silently affect the item entity's actual stack.
release
Wizards of Lua 4.0.2-1.21.4
4.0.2-1.21.45 апр. 2026 г.41Minecraft: 1.21.4fabric
Что нового
- **Fix: `ItemEntity.item` getter returned a live reference instead of a copy** (related to [#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- `ItemEntity.item` now returns a **copy** of the item stack, consistent with all other item getters introduced in 4.0.0.
- Previously, mutating the returned value would silently affect the item entity's actual stack.
release
Wizards of Lua 4.0.2-1.21.1
4.0.2-1.21.15 апр. 2026 г.43Minecraft: 1.21.1fabric
Что нового
- **Fix: `ItemEntity.item` getter returned a live reference instead of a copy** (related to [#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- `ItemEntity.item` now returns a **copy** of the item stack, consistent with all other item getters introduced in 4.0.0.
- Previously, mutating the returned value would silently affect the item entity's actual stack.
release
Wizards of Lua 4.0.1-1.21.11
4.0.1-1.21.113 апр. 2026 г.37Minecraft: 1.21.11fabric
Что нового
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- The fix in 4.0.0 was incomplete: the rembulan jar included at release time predated the actual compiler fix.
- Upgraded to **rembulan 0.3.3** which includes the correct fix in `BranchInlinerVisitor`.
release
Wizards of Lua 4.0.1-1.21.10
4.0.1-1.21.103 апр. 2026 г.27Minecraft: 1.21.10fabric
Что нового
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- The fix in 4.0.0 was incomplete: the rembulan jar included at release time predated the actual compiler fix.
- Upgraded to **rembulan 0.3.3** which includes the correct fix in `BranchInlinerVisitor`.
release
Wizards of Lua 4.0.1-1.21.8
4.0.1-1.21.83 апр. 2026 г.27Minecraft: 1.21.8fabric
Что нового
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- The fix in 4.0.0 was incomplete: the rembulan jar included at release time predated the actual compiler fix.
- Upgraded to **rembulan 0.3.3** which includes the correct fix in `BranchInlinerVisitor`.
release
Wizards of Lua 4.0.1-1.21.4
4.0.1-1.21.43 апр. 2026 г.16Minecraft: 1.21.4fabric
Что нового
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- The fix in 4.0.0 was incomplete: the rembulan jar included at release time predated the actual compiler fix.
- Upgraded to **rembulan 0.3.3** which includes the correct fix in `BranchInlinerVisitor`.
release
Wizards of Lua 4.0.1-1.21.1
4.0.1-1.21.13 апр. 2026 г.25Minecraft: 1.21.1fabric
Что нового
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- The fix in 4.0.0 was incomplete: the rembulan jar included at release time predated the actual compiler fix.
- Upgraded to **rembulan 0.3.3** which includes the correct fix in `BranchInlinerVisitor`.
release
Wizards of Lua 4.0.0-1.21.11
4.0.0-1.21.112 апр. 2026 г.27Minecraft: 1.21.11fabric
Что нового
- **Breaking Change: Item access now always returns and stores copies** ([#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- All item getters return a **copy** of the underlying item. Modifying the returned value does **not** affect the original.
- All item setters store a **copy** of the provided item. The original value passed in is not consumed or modified.
- **Migration:** Code that relied on in-place modification of item references must now write the value back explicitly. For example:
```lua
-- Before (no longer works):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- had an effect on the actual item in hand
-- After (correct):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- has NO effect on the actual item in hand
player:setItemInHand("MAIN_HAND", item) -- NOW affects the item in hand
```
- Affected APIs: `LivingEntity:getItemInHand()`, `LivingEntity:setItemInHand()`, `LivingEntity:getEquipment()`, `LivingEntity:setEquipment()`, `Player.mainHandItem`, `PlayerInventory[slot]`, `Entity:dropItem()`, `ItemEntity:new()`, `ItemEntity.item`.
- **New: `spell.world` is now writable** ([#25](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/25))
- Assign a world to move a spell to a different dimension:
```lua
spell.world = spell.server:getWorld("the_nether")
```
- Chunk-force tickets are correctly transferred when the spell changes worlds.
- **New: `Server.worlds`**
- Read-only list of all currently loaded server worlds.
- **New: `World.timeOfDay`**
- Read-only property returning the current time-of-day tick (0–23999).
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- Upgraded the internal Lua runtime (**rembulan 0.3.2**) which contains the fix for this logic bug.
release
Wizards of Lua 4.0.0-1.21.10
4.0.0-1.21.102 апр. 2026 г.20Minecraft: 1.21.10fabric
Что нового
- **Breaking Change: Item access now always returns and stores copies** ([#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- All item getters return a **copy** of the underlying item. Modifying the returned value does **not** affect the original.
- All item setters store a **copy** of the provided item. The original value passed in is not consumed or modified.
- **Migration:** Code that relied on in-place modification of item references must now write the value back explicitly. For example:
```lua
-- Before (no longer works):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- had an effect on the actual item in hand
-- After (correct):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- has NO effect on the actual item in hand
player:setItemInHand("MAIN_HAND", item) -- NOW affects the item in hand
```
- Affected APIs: `LivingEntity:getItemInHand()`, `LivingEntity:setItemInHand()`, `LivingEntity:getEquipment()`, `LivingEntity:setEquipment()`, `Player.mainHandItem`, `PlayerInventory[slot]`, `Entity:dropItem()`, `ItemEntity:new()`, `ItemEntity.item`.
- **New: `spell.world` is now writable** ([#25](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/25))
- Assign a world to move a spell to a different dimension:
```lua
spell.world = spell.server:getWorld("the_nether")
```
- Chunk-force tickets are correctly transferred when the spell changes worlds.
- **New: `Server.worlds`**
- Read-only list of all currently loaded server worlds.
- **New: `World.timeOfDay`**
- Read-only property returning the current time-of-day tick (0–23999).
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- Upgraded the internal Lua runtime (**rembulan 0.3.2**) which contains the fix for this logic bug.
release
Wizards of Lua 4.0.0-1.21.8
4.0.0-1.21.82 апр. 2026 г.14Minecraft: 1.21.8fabric
Что нового
- **Breaking Change: Item access now always returns and stores copies** ([#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- All item getters return a **copy** of the underlying item. Modifying the returned value does **not** affect the original.
- All item setters store a **copy** of the provided item. The original value passed in is not consumed or modified.
- **Migration:** Code that relied on in-place modification of item references must now write the value back explicitly. For example:
```lua
-- Before (no longer works):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- had an effect on the actual item in hand
-- After (correct):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- has NO effect on the actual item in hand
player:setItemInHand("MAIN_HAND", item) -- NOW affects the item in hand
```
- Affected APIs: `LivingEntity:getItemInHand()`, `LivingEntity:setItemInHand()`, `LivingEntity:getEquipment()`, `LivingEntity:setEquipment()`, `Player.mainHandItem`, `PlayerInventory[slot]`, `Entity:dropItem()`, `ItemEntity:new()`, `ItemEntity.item`.
- **New: `spell.world` is now writable** ([#25](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/25))
- Assign a world to move a spell to a different dimension:
```lua
spell.world = spell.server:getWorld("the_nether")
```
- Chunk-force tickets are correctly transferred when the spell changes worlds.
- **New: `Server.worlds`**
- Read-only list of all currently loaded server worlds.
- **New: `World.timeOfDay`**
- Read-only property returning the current time-of-day tick (0–23999).
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- Upgraded the internal Lua runtime (**rembulan 0.3.2**) which contains the fix for this logic bug.
release
Wizards of Lua 4.0.0-1.21.4
4.0.0-1.21.42 апр. 2026 г.19Minecraft: 1.21.4fabric
Что нового
- **Breaking Change: Item access now always returns and stores copies** ([#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- All item getters return a **copy** of the underlying item. Modifying the returned value does **not** affect the original.
- All item setters store a **copy** of the provided item. The original value passed in is not consumed or modified.
- **Migration:** Code that relied on in-place modification of item references must now write the value back explicitly. For example:
```lua
-- Before (no longer works):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- had an effect on the actual item in hand
-- After (correct):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- has NO effect on the actual item in hand
player:setItemInHand("MAIN_HAND", item) -- NOW affects the item in hand
```
- Affected APIs: `LivingEntity:getItemInHand()`, `LivingEntity:setItemInHand()`, `LivingEntity:getEquipment()`, `LivingEntity:setEquipment()`, `Player.mainHandItem`, `PlayerInventory[slot]`, `Entity:dropItem()`, `ItemEntity:new()`, `ItemEntity.item`.
- **New: `spell.world` is now writable** ([#25](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/25))
- Assign a world to move a spell to a different dimension:
```lua
spell.world = spell.server:getWorld("the_nether")
```
- Chunk-force tickets are correctly transferred when the spell changes worlds.
- **New: `Server.worlds`**
- Read-only list of all currently loaded server worlds.
- **New: `World.timeOfDay`**
- Read-only property returning the current time-of-day tick (0–23999).
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- Upgraded the internal Lua runtime (**rembulan 0.3.2**) which contains the fix for this logic bug.
release
Wizards of Lua 4.0.0-1.21.1
4.0.0-1.21.12 апр. 2026 г.26Minecraft: 1.21.1fabric
Что нового
- **Breaking Change: Item access now always returns and stores copies** ([#26](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/26))
- All item getters return a **copy** of the underlying item. Modifying the returned value does **not** affect the original.
- All item setters store a **copy** of the provided item. The original value passed in is not consumed or modified.
- **Migration:** Code that relied on in-place modification of item references must now write the value back explicitly. For example:
```lua
-- Before (no longer works):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- had an effect on the actual item in hand
-- After (correct):
local item = player:getItemInHand("MAIN_HAND")
item.count = 5 -- has NO effect on the actual item in hand
player:setItemInHand("MAIN_HAND", item) -- NOW affects the item in hand
```
- Affected APIs: `LivingEntity:getItemInHand()`, `LivingEntity:setItemInHand()`, `LivingEntity:getEquipment()`, `LivingEntity:setEquipment()`, `Player.mainHandItem`, `PlayerInventory[slot]`, `Entity:dropItem()`, `ItemEntity:new()`, `ItemEntity.item`.
- **New: `spell.world` is now writable** ([#25](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/25))
- Assign a world to move a spell to a different dimension:
```lua
spell.world = spell.server:getWorld("the_nether")
```
- Chunk-force tickets are correctly transferred when the spell changes worlds.
- **New: `Server.worlds`**
- Read-only list of all currently loaded server worlds.
- **New: `World.timeOfDay`**
- Read-only property returning the current time-of-day tick (0–23999).
- **Fix: `and`/`or` short-circuit chain returns wrong result when condition is a comparison** ([#27](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/27))
- Upgraded the internal Lua runtime (**rembulan 0.3.2**) which contains the fix for this logic bug.
release
Wizards of Lua 3.0.0-1.21.10
3.0.0-1.21.105 дек. 2025 г.127Minecraft: 1.21.10fabric
Что нового
- **New: `WizardsOfLua.global` (shared runtime state)**
- A read-only **reference** to a mutable, global **table** available to all spells during the current server runtime.
- Any spell can write to this table; all other spells see the changes immediately.
- The table is **cleared** on server restart or when running **`/wol startup`**.
- **Breaking Change: `WizardsOfLua.setCommand(...)`**
- The previous boolean return value has been **removed**.
- The function now **throws an error** if command registration fails.
- **Dynamic Commands: redesigned registration**
- Internal registration no longer relies on **`/reload`** → improves compatibility with **Arclight** and other mods that break on reload.
- **Replacing** existing **non-dynamic** commands (vanilla or from other mods) is **no longer supported**.
- **Extending** existing commands remains **supported**.
- **Fix:** `/wol spell list` from server console
- Resolved unexpected error when executing from the **server console**. See [Issue #24](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/24).
release
Wizards of Lua 3.0.0-1.21.8
3.0.0-1.21.85 дек. 2025 г.56Minecraft: 1.21.8fabric
Что нового
- **New: `WizardsOfLua.global` (shared runtime state)**
- A read-only **reference** to a mutable, global **table** available to all spells during the current server runtime.
- Any spell can write to this table; all other spells see the changes immediately.
- The table is **cleared** on server restart or when running **`/wol startup`**.
- **Breaking Change: `WizardsOfLua.setCommand(...)`**
- The previous boolean return value has been **removed**.
- The function now **throws an error** if command registration fails.
- **Dynamic Commands: redesigned registration**
- Internal registration no longer relies on **`/reload`** → improves compatibility with **Arclight** and other mods that break on reload.
- **Replacing** existing **non-dynamic** commands (vanilla or from other mods) is **no longer supported**.
- **Extending** existing commands remains **supported**.
- **Fix:** `/wol spell list` from server console
- Resolved unexpected error when executing from the **server console**. See [Issue #24](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/24).
release
Wizards of Lua 3.0.0-1.21.4
3.0.0-1.21.45 дек. 2025 г.53Minecraft: 1.21.4fabric
Что нового
- **New: `WizardsOfLua.global` (shared runtime state)**
- A read-only **reference** to a mutable, global **table** available to all spells during the current server runtime.
- Any spell can write to this table; all other spells see the changes immediately.
- The table is **cleared** on server restart or when running **`/wol startup`**.
- **Breaking Change: `WizardsOfLua.setCommand(...)`**
- The previous boolean return value has been **removed**.
- The function now **throws an error** if command registration fails.
- **Dynamic Commands: redesigned registration**
- Internal registration no longer relies on **`/reload`** → improves compatibility with **Arclight** and other mods that break on reload.
- **Replacing** existing **non-dynamic** commands (vanilla or from other mods) is **no longer supported**.
- **Extending** existing commands remains **supported**.
- **Fix:** `/wol spell list` from server console
- Resolved unexpected error when executing from the **server console**. See [Issue #24](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/24).
release
Wizards of Lua 3.0.0-1.21.1
3.0.0-1.21.15 дек. 2025 г.116Minecraft: 1.21.1fabric
Что нового
- **New: `WizardsOfLua.global` (shared runtime state)**
- A read-only **reference** to a mutable, global **table** available to all spells during the current server runtime.
- Any spell can write to this table; all other spells see the changes immediately.
- The table is **cleared** on server restart or when running **`/wol startup`**.
- **Breaking Change: `WizardsOfLua.setCommand(...)`**
- The previous boolean return value has been **removed**.
- The function now **throws an error** if command registration fails.
- **Dynamic Commands: redesigned registration**
- Internal registration no longer relies on **`/reload`** → improves compatibility with **Arclight** and other mods that break on reload.
- **Replacing** existing **non-dynamic** commands (vanilla or from other mods) is **no longer supported**.
- **Extending** existing commands remains **supported**.
- **Fix:** `/wol spell list` from server console
- Resolved unexpected error when executing from the **server console**. See [Issue #24](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/24).
release
Wizards of Lua 2.10.2-1.21.8
2.10.2-1.21.821 нояб. 2025 г.42Minecraft: 1.21.8fabric
Что нового
- **Fix:** `LuaEntity.name` now returns `getName().getString()` as intended.
- **Fix:** `Vec3:floor()` now correctly converts all components to integers.
release
Wizards of Lua 2.10.2-1.21.10
2.10.2-1.21.1020 нояб. 2025 г.70Minecraft: 1.21.10fabric
Что нового
- **Fix:** `LuaEntity.name` now returns `getName().getString()` as intended.
- **Fix:** `Vec3:floor()` now correctly converts all components to integers.
release
Wizards of Lua 2.10.2-1.21.4
2.10.2-1.21.420 нояб. 2025 г.27Minecraft: 1.21.4fabric
Что нового
- **Fix:** `LuaEntity.name` now returns `getName().getString()` as intended.
- **Fix:** `Vec3:floor()` now correctly converts all components to integers.
release
Wizards of Lua 2.10.2-1.21.1
2.10.2-1.21.120 нояб. 2025 г.56Minecraft: 1.21.1fabric
Что нового
- **Fix:** `LuaEntity.name` now returns `getName().getString()` as intended.
- **Fix:** `Vec3:floor()` now correctly converts all components to integers.
release
Wizards of Lua 2.10.1-1.21.10
2.10.1-1.21.1026 окт. 2025 г.94Minecraft: 1.21.10fabric
Что нового
- **Fix `/wol extract examples`:**
Modified files from the extracted **Journey** example are now loaded correctly. Previously, the mod loaded the bundled copy from the JAR instead of the filesystem, so user edits had no effect.
- **Fix (Journey Mini-Game):**
- The Journey spell now terminates properly when a player disconnects from the server.
- Disabled verbose log output by default in the Journey mini-game.
You can re-enable it by editing `config/wizards-of-lua/examples/journey/log_extensions.log` and setting `logEnabled = true`.
release
Wizards of Lua 2.10.1-1.21.9
2.10.1-1.21.926 окт. 2025 г.48Minecraft: 1.21.9fabric
Что нового
- **Fix `/wol extract examples`:**
Modified files from the extracted **Journey** example are now loaded correctly. Previously, the mod loaded the bundled copy from the JAR instead of the filesystem, so user edits had no effect.
- **Fix (Journey Mini-Game):**
- The Journey spell now terminates properly when a player disconnects from the server.
- Disabled verbose log output by default in the Journey mini-game.
You can re-enable it by editing `config/wizards-of-lua/examples/journey/log_extensions.log` and setting `logEnabled = true`.
release
Wizards of Lua 2.10.1-1.21.8
2.10.1-1.21.818 сент. 2025 г.87Minecraft: 1.21.8fabric
Что нового
- **Fix `/wol extract examples`:**
Modified files from the extracted **Journey** example are now loaded correctly. Previously, the mod loaded the bundled copy from the JAR instead of the filesystem, so user edits had no effect.
- **Fix (Journey Mini-Game):**
- The Journey spell now terminates properly when a player disconnects from the server.
- Disabled verbose log output by default in the Journey mini-game.
You can re-enable it by editing `config/wizards-of-lua/examples/journey/log_extensions.log` and setting `logEnabled = true`.
release
Wizards of Lua 2.10.0-1.21.8
2.10.0-1.21.814 сент. 2025 г.45Minecraft: 1.21.8fabric
Что нового
- **New Command for Extracting Examples**
- **`/wol extract examples`** populates **`config/wizards-of-lua/examples/`** with example projects; currently includes the mini-game **“Journey.”**
- After extraction run **`/wol startup`** to register the example’s dynamic commands.
- **Command Changes**
- Extraction syntax updated: use **`/wol extract api`** (replaces the old **`/wol extract-api`**).
- **New Fields on `Spell`**
- `tickLimit` — Per-tick execution budget (in Lua operations). Default **50,000**; higher values do more work per tick (potential TPS impact), lower values yield more often for smoother pacing.
- `biome` — **read-only** biome ID at `spell.pos` (e.g., `"plains"`, `"forest"`); updates as the spell moves.
release
Wizards of Lua 2.9.3-1.21.8
2.9.3-1.21.86 сент. 2025 г.44Minecraft: 1.21.8fabric
Что нового
- **Fix:** Resolved a NullPointerException in `Spell:copyStructure(oppositeCorner [, includeEntities, ignoreBlockId])` when `ignoreBlockId` was not specified.
release
Wizards of Lua 2.9.2-1.21.8
2.9.2-1.21.84 сент. 2025 г.48Minecraft: 1.21.8fabric
Что нового
- **Fix:** `LivingEntity:getItemInHand(hand)` no longer throws a NullPointerException when the specified hand is empty.
- **API Docs:** Adjusted example code that handles NBT data to use the new Minecraft **1.21.5** NBT syntax in:
- `BlockEntity`
- `Entity`
- `Item`
- `Spell`
- **Note:** Run `/wol extract-api` to update the `config/wizards-oflua/api` folder with the latest API documentation.
release
Wizards of Lua 2.9.1-1.21.8
2.9.1-1.21.815 авг. 2025 г.81Minecraft: 1.21.8fabric
Что нового
- **Fix:** Setting `spell.visible = true` no longer throws an exception.
release
Wizards of Lua 2.9.1-1.21.4
2.9.1-1.21.415 авг. 2025 г.64Minecraft: 1.21.4fabric
Что нового
- **Fix:** Setting `spell.visible = true` no longer throws an exception.
release
Wizards of Lua 2.9.0-1.21.8
2.9.0-1.21.812 авг. 2025 г.55Minecraft: 1.21.8fabric
Что нового
- **New Function: `Server:getWorld(dimension)`**
Returns a reference to the `World` for the given dimension name (e.g., `"the_nether"`, `"overworld"`).
- **New Property: `Spell.id`**
Read-only numeric ID of the spell.
- **Updated Methods: `Spell:execute(...)` and `Spell:executeSilent(...)`**
Both now return the **command result value** (typically the number of affected entities/blocks).
- **Fix: `World:isLoaded(pos)`**
Corrected a bug where this function could incorrectly return `true` in some cases; behavior now matches the documentation.
- **Fix: `EventInterceptor:stop()`**
This method now actually stops the interceptor from receiving further events.
- **New Events**
Added hooks to observe or intercept more gameplay actions:
`AfterLivingEntityDamageEvent`, `BeforeLivingEntityDamageEvent`, `EntityDespawnEvent`, `EntitySpawnEvent`, `PlayerAttackBlockEvent`, `PlayerChangeWorldEvent`, `PlayerDisconnectedEvent`, `PlayerDropSelectedItemEvent`, `PlayerUseEntityEvent`.
release
Wizards of Lua 2.9.0-1.21.4
2.9.0-1.21.411 авг. 2025 г.43Minecraft: 1.21.4fabric
Что нового
- **New Function: `Server:getWorld(dimension)`**
Returns a reference to the `World` for the given dimension name (e.g., `"the_nether"`, `"overworld"`).
- **New Property: `Spell.id`**
Read-only numeric ID of the spell.
- **Updated Methods: `Spell:execute(...)` and `Spell:executeSilent(...)`**
Both now return the **command result value** (typically the number of affected entities/blocks).
- **Fix: `World:isLoaded(pos)`**
Corrected a bug where this function could incorrectly return `true` in some cases; behavior now matches the documentation.
- **Fix: `EventInterceptor:stop()`**
This method now actually stops the interceptor from receiving further events.
- **New Events**
Added hooks to observe or intercept more gameplay actions:
`AfterLivingEntityDamageEvent`, `BeforeLivingEntityDamageEvent`, `EntityDespawnEvent`, `EntitySpawnEvent`, `PlayerAttackBlockEvent`, `PlayerChangeWorldEvent`, `PlayerDisconnectedEvent`, `PlayerDropSelectedItemEvent`, `PlayerUseEntityEvent`.
release
Wizards of Lua 2.9.0-1.21.2
2.9.0-1.21.211 авг. 2025 г.41Minecraft: 1.21.2fabric
Что нового
- **New Function: `Server:getWorld(dimension)`**
Returns a reference to the `World` for the given dimension name (e.g., `"the_nether"`, `"overworld"`).
- **New Property: `Spell.id`**
Read-only numeric ID of the spell.
- **Updated Methods: `Spell:execute(...)` and `Spell:executeSilent(...)`**
Both now return the **command result value** (typically the number of affected entities/blocks).
- **Fix: `World:isLoaded(pos)`**
Corrected a bug where this function could incorrectly return `true` in some cases; behavior now matches the documentation.
- **Fix: `EventInterceptor:stop()`**
This method now actually stops the interceptor from receiving further events.
- **New Events**
Added hooks to observe or intercept more gameplay actions:
`AfterLivingEntityDamageEvent`, `BeforeLivingEntityDamageEvent`, `EntityDespawnEvent`, `EntitySpawnEvent`, `PlayerAttackBlockEvent`, `PlayerChangeWorldEvent`, `PlayerDisconnectedEvent`, `PlayerDropSelectedItemEvent`, `PlayerUseEntityEvent`.
release
Wizards of Lua 2.9.0-1.21.1
2.9.0-1.21.111 авг. 2025 г.105Minecraft: 1.21.1fabric
Что нового
- **New Function: `Server:getWorld(dimension)`**
Returns a reference to the `World` for the given dimension name (e.g., `"the_nether"`, `"overworld"`).
- **New Property: `Spell.id`**
Read-only numeric ID of the spell.
- **Updated Methods: `Spell:execute(...)` and `Spell:executeSilent(...)`**
Both now return the **command result value** (typically the number of affected entities/blocks).
- **Fix: `World:isLoaded(pos)`**
Corrected a bug where this function could incorrectly return `true` in some cases; behavior now matches the documentation.
- **Fix: `EventInterceptor:stop()`**
This method now actually stops the interceptor from receiving further events.
- **New Events**
Added hooks to observe or intercept more gameplay actions:
`AfterLivingEntityDamageEvent`, `BeforeLivingEntityDamageEvent`, `EntityDespawnEvent`, `EntitySpawnEvent`, `PlayerAttackBlockEvent`, `PlayerChangeWorldEvent`, `PlayerDisconnectedEvent`, `PlayerDropSelectedItemEvent`, `PlayerUseEntityEvent`.
release
Wizards of Lua 2.8.0-1.21.1
2.8.0-1.21.125 июл. 2025 г.55Minecraft: 1.21.1fabric
Что нового
- **New Field: `Server.players`**
A `Player[]` @Read-Only list of references to all currently online players.
- **New Function: `Player:sendCommandTree()`**
Sends the current command tree to the player's Minecraft client, forcing Brigadier to rebuild and resend the set of available commands (useful after permission changes).
- **Dynamic Commands: permission-based suggestion filtering**
Fixed an issue where Brigadier suggestions for dynamic commands could appear even if the player lacked permission. Suggestions are now correctly filtered by the player’s permission level. Thanks to **Nurio** for reporting this issue.
release
Wizards of Lua 2.7.1-1.21.1
2.7.1-1.21.119 июл. 2025 г.49Minecraft: 1.21.1fabric
Что нового
- **Docs: Field Annotations Reviewed (revised)**
Re-reviewed and updated documentation for *every* field to ensure clarity on whether accessing a value returns a reference or a copy. No fields missing this time.
- **Fix:** Client crash upon dropping an item ([#21](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/issues/21))
Resolved the crash that occurred when dropping an item on the client. Thanks to **Nurio** for reporting this issue.
release
Wizards of Lua 2.7.0-1.21.1
2.7.0-1.21.118 июл. 2025 г.37Minecraft: 1.21.1fabric
Что нового
- **Docs: Class Section Formatting**
Updated all API docs so that each class description appears *above* the `---@class` tag, in accordance with our documentation standards.
- **Docs: Field Documentation Reviewed**
- Reviewed and revised documentation for every field to clearly indicate whether accessing it returns a **reference** or a **copy**.
- Added missing `@Read-Only` tags for all immutable properties.
- **Docs: Example Code**
Added more illustrative code snippets throughout the API docs to demonstrate common usage patterns and clarify behavior.
- **Note:** Run `/wol extract-api` to update the `config/wizards-of-lua/api` folder with the latest API documentation.
release
Wizards of Lua 2.6.0-1.21.1
2.6.0-1.21.113 июл. 2025 г.45Minecraft: 1.21.1fabric
Что нового
- **Dynamic Commands: New Enum-String Placeholder**
Added support for an enum-string placeholder (`%s[opt1|opt2|…]`) that restricts string inputs to a given set, enabling built-in tab-completion and automatic validation of allowed values.
- **Standard `io` Module Support**
Wizards of Lua now officially supports Lua’s standard `io` module, allowing scripts to create, read, and update files in the server’s world folder.
- **New Class: `Structure`**
Represents a captured box-shaped region of the world.
- **New Class: `Server`**
Provides server-level operations:
- `Server:loadStructure(name)` — Load a saved `Structure` by name.
- `Server:saveStructure(name, structure)` — Save a `Structure` to disk.
- **New Functions on `Spell`**
- `Spell:copyStructure(oppositeCorner [, includeEntities, ignoreBlockId])` — Captures a box-shaped region into a `Structure` object.
- `Spell:pasteStructure(structure [, originPos, ignoreBlockIds])` — Pastes a `Structure` at the spell’s position.
- `Spell:setYaw(direction)` — Sets the spell’s yaw to face a given compass direction (`"north"`, `"south"`, `"west"`, `"east"`).
- **New Properties on `Spell`**
- `Spell.facing` — Read-only compass direction the spell is oriented toward (`"north"`, `"east"`, `"south"`, `"west"`).
- `Spell.server` — Reference to the `Server` object for accessing server-level operations (filesystem, version, structures).
- **Updated Method: `Spell:move`**
Now accepts **relative** directions (`"forward"`, `"back"`, `"left"`, `"right"`) in addition to absolute directions to execute relative movement.
- **New Property: `Entity.facing`**
Exposes the entity’s current compass direction.
- **New Event: `SpellFinishEvent`**
Fired immediately before a spell completes execution, allowing interceptors to perform cleanup tasks.
- **Refactoring:**
Under-the-hood refactoring of the internal spell lifecycle management for improved stability and consistency.
release
Wizards of Lua 2.5.0-1.21.1
2.5.0-1.21.16 июл. 2025 г.43Minecraft: 1.21.1fabric
Что нового
- **New Feature: Dynamic Commands**
Register, inspect and remove custom commands at runtime via the `WizardsOfLua` module API:
- `WizardsOfLua.setCommand({ id, level, pattern, code })` to add a command.
- `WizardsOfLua.listCommands()` to list all dynamic commands.
- `WizardsOfLua.removeCommand(id)` to unregister a command.
Patterns use Brigadier-style syntax with named or unnamed placeholders—see the API docs for full details and examples.
- **Fix:** PrintRedirector & LogRedirector now strip carriage return characters from their output.
- **Fix:** Wol commands updated to include intermediate permission checks.
- **Fix:** Examples section: bug in Auto-Toggle Gamemode Field fixed.
release
Wizards of Lua 2.4.0-1.21.1
2.4.0-1.21.121 июн. 2025 г.52Minecraft: 1.21.1fabric
Что нового
- **New Property: `Entity.extra`**
Introduces a persistent `extra` table on every entity for custom data, saved as NBT into the entity’s NBT storage on disk.
- **New Method: `Entity:putExtra(...)`**
Merges a table into an entity’s `extra` field for persisting values.
- **New Events: `ChunkLoadEvent`, `ChunkUnloadEvent`**
Fired whenever a chunk loads into or unloads from memory.
- **New Field: `World.spawnPos`**
Exposes the world’s spawn point as a `Vec3`.
- **Updated Event: `ChatMessageEvent`**
Now cancelable. Return `false` in an interceptor callback to prevent broadcasting.
- **Updated API Docs: `AfterPlayerRespawnEvent`**
Added example usage demonstrating how to queue and handle the event.
release
Wizards of Lua 2.3.0-1.21.1
2.3.0-1.21.115 июн. 2025 г.40Minecraft: 1.21.1fabric
Что нового
- **New Event: `PermissionCheckEvent`**
Can now intercept permission checks and override the default permission system (e.g. LuckPerms) from within Lua.
- In intercept callbacks, assign `evt.allowed = true` to grant or `false` to deny.
- Leaving `evt.allowed` as `nil` defers to the default permission logic.
- **Refined event handling in interceptors**
Returning `nil` from an interceptor callback is now permitted and treated as `true`, so the event will continue (i.e., not be canceled).
- **Fix:**
- `WolStartupCommand` and `WolExtractApiCommand` permissions were not assigned correctly; now fixed.
release
Wizards of Lua 2.2.0-1.21.1
2.2.0-1.21.19 июн. 2025 г.41Minecraft: 1.21.1fabric
Что нового
- **New Class: `PlayerInventory`**
The player's inventory can now be accessed programmatically through the `Player.inventory` property.
- Slot indices follow vanilla layout:
- `0–8`: hotbar
- `9–35`: main inventory
- `36–39`: armor slots (feet → head)
- `40`: offhand
- Use `player.inventory[i]` to read/write items.
- Access the currently selected hotbar index via `inventory.selectedSlot`.
- New method: `PlayerInventory:clear()` wipes all slots.
- **New Method: `Item:copy()`**
Creates a deep copy of the item.
Useful for duplicating stacks or preserving originals before modification.
- **New Methods for Equipment Handling** on `LivingEntity`:
- `LivingEntity:getEquipment(slot)`
Returns the `Item` in a specific equipment slot.
- `LivingEntity:setEquipment(slot, item)`
Places an item into the specified slot.
> **Slot Names**: `"mainhand"`, `"offhand"`, `"feet"`, `"legs"`, `"chest"`, `"head"`, `"body"`
> (Use lowercase names as strings.)
release
Wizards of Lua 2.1.0-1.21.1
2.1.0-1.21.131 мая 2025 г.82Minecraft: 1.21.1fabric
Что нового
- **Command Permissions Support**
Wizards of Lua now supports Minecraft-style permission nodes for all commands.
- Each command (e.g., `/lua`, `/wol extract-api`) is gated by a permission node such as `wizardsoflua.lua` or `wizardsoflua.wol.extract-api`.
- By default, these commands require operator level 2.
- On servers using permission plugins like [LuckPerms](https://luckperms.net), access can be granted per node.
- See the [full list of permission nodes and required levels](https://codeberg.org/mickkay/Wizards-of-Lua-Fabric/src/branch/main/COMMANDS.md).
release
Wizards of Lua 2.0.0-1.21.1
2.0.0-1.21.125 мая 2025 г.79Minecraft: 1.21.1fabric
Что нового
- **Semantic Versioning** introduced. This project now follows the `MAJOR.MINOR.PATCH` format.
- **Removed Command**: `wol printMode` has been removed.
- **Output Behavior**:
- The Lua `print(...)` function now sends its output as **standard command feedback**.
- New `log(...)` function introduced for logging messages. Output is routed based on the `log` field in the `WizardsOfLua` module.
- **New Class**: `Trace`
- Use `Trace` objects to programmatically capture command output.
- **Updated Function**: `Spell:execute(...)`
- No longer returns a value.
- Command output is now sent as standard command feedback.
- To capture feedback, pass a `Trace` object as the optional second argument.
- Now supports multiple calls within the same game tick.
- **New Function**: `Spell:executeSilent(...)`
- Executes a command without sending output as feedback.
- Supports the same `Trace` capture as `Spell:execute(...)`.
- **Changes in Module `WizardsOfLua`**:
- Removed deprecated `printMode` field.
- Added new `log` field to control the output target of `log(...)`.
release
Wizards of Lua 1.8.1-1.21.1
1.8.1-1.21.122 мая 2025 г.86Minecraft: 1.21.1fabric
Что нового
* Fix error: Registry entry (wizardsoflua:print_mode) is missing from local registry (minecraft:command_argument_type)
release
Wizards of Lua 1.8.0-1.21.1
1.8.0-1.21.120 мая 2025 г.52Minecraft: 1.21.1fabric
Что нового
* New command: `/wol spell list`
* New command: `/wol printMode`
* New module `WizardsOfLua` to read and write configuration settings.
* New event: `PlayerAttackEntityEvent`
* New example: Vertical Minecart using ladders
* New example: Safezone spell
* Fix Player: rename incorrect class name - ServerPlayerEntity -> Player
* New field: `Player.permissionLevel`
* Add documentation of commands
* Vec3: Add documentation for `Vec3(x,y,z)`
* Remove `Done.` output after spell creation.
release
Wizards of Lua 1.7.0-1.21.1
1.7.0-1.21.117 мая 2025 г.74Minecraft: 1.21.1fabric
Что нового
* Frequently Asked Questions: add FAQ markdown file
* World: add "dimension" property
release
Wizards of Lua 1.6.1-1.21.1
1.6.1-1.21.114 мая 2025 г.75Minecraft: 1.21.1fabric
Что нового
* Fix Vec3: revert removal of type declaration
* /wol startup: the server should be the spell.owner
release
Wizards of Lua 1.6.0-1.21.1
1.6.0-1.21.114 мая 2025 г.58Minecraft: 1.21.1fabric
Что нового
* Add Entity.uuid
* Fix Player: mainHandItem should be nil rather than ItemStack.EMPTY
release
Wizards of Lua 1.5.0-1.21.1
1.5.0-1.21.114 мая 2025 г.80Minecraft: 1.21.1fabric
Что нового
* Add Placeholder API support
release
Wizards of Lua 1.4.1-1.21.1
1.4.1-1.21.16 мая 2025 г.84Minecraft: 1.21.1fabric
Что нового
* Improved Wizards of Lua API docs
release
Wizards of Lua 1.4.0-1.21.1
1.4.0-1.21.13 мая 2025 г.72Minecraft: 1.21.1fabric
Что нового
* Startup: kill any active spells on startup (Singleplayer)
* Wizards of Lua API: more examples, better explanation
release
Wizards of Lua 1.3.1-1.21.1
1.3.1-1.21.126 апр. 2025 г.97Minecraft: 1.21.1fabric
Что нового
* Fix LuaEntity: invalid downcast to ItemEntity in "putNbt" function
* Remove LuaPig: undocumented class, existed for testing only.
release
Wizards of Lua 1.3.0-1.21.1
1.3.0-1.21.125 апр. 2025 г.75Minecraft: 1.21.1fabric
Что нового
* Item: add optional constructor parameter "nbt"
* Item: add "putNbt" function
* ItemEntity: add optional constructor parameter "nbt"
release
Wizards of Lua 1.2.0-1.21.1
1.2.0-1.21.13 апр. 2025 г.111Minecraft: 1.21.1fabric
Что нового
* LivingEntity: new read-only property `baby`
* LivingEntity: new read-only property `hostile`
* _globals: new function `instanceOf`
* new example: Thunder Stick
release
Wizards of Lua 1.1.0-1.21.1
1.1.0-1.21.131 мар. 2025 г.71Minecraft: 1.21.1fabric
Что нового
* ItemEntity: add "new" function
release
Wizards of Lua 1.0.0-1.21.1
1.0.0-1.21.123 мар. 2025 г.88Minecraft: 1.21.1fabric
Что нового
- Initial version 1.0.0 of the farbic version of Wizards of Lua.
- Written for Minecraft 1.21.1