fix(mod-api): validate active block storage

This commit is contained in:
Bo Layer
2026-08-23 03:20:43 -06:00
parent 3b7fc42858
commit d17f0725d8
4 changed files with 15 additions and 3 deletions
+2 -2
View File
@@ -913,8 +913,8 @@ M.VERMILION_DOCK = {
-- the ship is long gone: erase her right away, and anyone who
-- still lands here is sent back out past the guard unless a mod
-- explicitly permits this occupied map state. This hook surrounds
-- only the ejection decision; the map's complete onEnter chain has
-- already run and the departed ship remains erased.
-- only the ejection decision; map-script registration and dispatch
-- stay unchanged, and the departed ship remains erased.
for _, b in ipairs(DOCK_SHIP_BLOCKS) do
ow.map:setBlock(b.bx, b.by, b.water)
end
+3
View File
@@ -247,6 +247,9 @@ or malformed active block returns `nil, "block unavailable"`. The caller must
require every expected cell to match before changing presentation. This method
is Gen 1-only; Gold callers receive no parity promise for it.
The same unavailable result covers missing or sparse active block storage and
an accessor result that does not match its validated active block slot.
### Conditional map occupancy
`map.occupancy_allowed` is a narrow Gen 1 hook around a map script's vanilla
@@ -89,6 +89,10 @@ Failure reasons are stable:
| Coordinate is negative or outside the active map | `block coordinates out of bounds` |
| Active block data is absent or malformed | `block unavailable` |
The block slot and the active map accessor must both contain the same valid
nonnegative integer. Missing or sparse storage and inconsistent accessor data
return `block unavailable`.
Requiring the expected map ID and rejecting all ambiguous input lets a mod
compare every cell in its version-specific signature before it calls an
existing mutation API. Red, Blue, and Yellow each use their own loaded map
+6 -1
View File
@@ -146,8 +146,13 @@ function WorldAPI:activeBlockAt(mapId, bx, by)
if type(def.blocks) ~= "table" or type(map.blockAt) ~= "function" then
return nil, "block unavailable"
end
local stored = def.blocks[by * def.width + bx + 1]
if not validBlockCoordinate(stored) or stored < 0 then
return nil, "block unavailable"
end
local ok, blockId = pcall(map.blockAt, map, bx, by)
if not ok or not validBlockCoordinate(blockId) or blockId < 0 then
if not ok or not validBlockCoordinate(blockId) or blockId < 0
or blockId ~= stored then
return nil, "block unavailable"
end
return blockId