mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
120 lines
2.8 KiB
Lua
120 lines
2.8 KiB
Lua
-- Luanti
|
|
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
|
-- Copyright (C) 2023 v-rob, Vincent Robinson <robinsonvincent89@gmail.com>
|
|
|
|
local next_id = 0
|
|
|
|
function ui.new_id()
|
|
-- Just increment a monotonic counter and return it as hex. Even at
|
|
-- unreasonably fast ID generation rates, it would take years for this
|
|
-- counter to hit the 2^53 limit and start generating duplicates.
|
|
next_id = next_id + 1
|
|
return string.format("_%X", next_id)
|
|
end
|
|
|
|
ui._ID_CHARS = "a-zA-Z0-9_%-%:"
|
|
|
|
function ui.is_id(str)
|
|
return type(str) == "string" and str == str:match("^[" .. ui._ID_CHARS .. "]+$")
|
|
end
|
|
|
|
function ui._is_reserved_id(str)
|
|
return ui.is_id(str) and str:match("^[_-]")
|
|
end
|
|
|
|
-- This coordinate size calculation copies the one for fixed-size formspec
|
|
-- coordinates in guiFormSpecMenu.cpp.
|
|
function ui.get_coord_size()
|
|
return math.floor(0.5555 * 96)
|
|
end
|
|
|
|
function ui._req(val, typ)
|
|
assert(type(val) == typ or
|
|
(typ == "id" and ui.is_id(val)) or core.is_instance(val, typ))
|
|
return val
|
|
end
|
|
|
|
function ui._opt(val, typ, def)
|
|
if val == nil then
|
|
return def
|
|
end
|
|
return ui._req(val, typ)
|
|
end
|
|
|
|
function ui._req_array(arr, typ)
|
|
for _, val in ipairs(ui._req(arr, "table")) do
|
|
ui._req(val, typ)
|
|
end
|
|
return arr
|
|
end
|
|
|
|
function ui._opt_array(arr, typ, def)
|
|
for _, val in ipairs(ui._opt(arr, "table", {})) do
|
|
ui._req(val, typ)
|
|
end
|
|
return arr or def
|
|
end
|
|
|
|
function ui._req_enum(val, enum)
|
|
assert(type(val) == "string" and enum[val])
|
|
return val
|
|
end
|
|
|
|
function ui._opt_enum(val, enum, def)
|
|
if val == nil then
|
|
return def
|
|
end
|
|
return ui._req_enum(val, enum)
|
|
end
|
|
|
|
ui._encode = core.encode_network
|
|
ui._decode = core.decode_network
|
|
|
|
function ui._encode_array(format, arr)
|
|
local formatted = {}
|
|
for _, val in ipairs(arr) do
|
|
table.insert(formatted, ui._encode(format, val))
|
|
end
|
|
|
|
return ui._encode("IZ", #formatted, table.concat(formatted))
|
|
end
|
|
|
|
function ui._pack_flags(...)
|
|
local flags = 0
|
|
for _, flag in ipairs({...}) do
|
|
flags = bit.bor(bit.lshift(flags, 1), flag and 1 or 0)
|
|
end
|
|
return flags
|
|
end
|
|
|
|
function ui._make_flags()
|
|
return {flags = 0, num_flags = 0, data = {}}
|
|
end
|
|
|
|
function ui._shift_flag(fl, flag)
|
|
-- OR the LSB with the condition, and then right rotate it to the MSB.
|
|
fl.flags = bit.ror(bit.bor(fl.flags, flag and 1 or 0), 1)
|
|
fl.num_flags = fl.num_flags + 1
|
|
|
|
return flag
|
|
end
|
|
|
|
function ui._shift_flag_bool(fl, flag)
|
|
if ui._shift_flag(fl, flag ~= nil) then
|
|
ui._shift_flag(fl, flag)
|
|
else
|
|
ui._shift_flag(fl, false)
|
|
end
|
|
end
|
|
|
|
function ui._encode_flag(fl, ...)
|
|
table.insert(fl.data, ui._encode(...))
|
|
end
|
|
|
|
function ui._encode_flags(fl)
|
|
-- We've been shifting into the right the entire time, so flags are in the
|
|
-- upper bits; however, the protocol expects them to be in the lower bits.
|
|
-- So, shift them the appropriate amount into the lower bits.
|
|
local adjusted = bit.rshift(fl.flags, 32 - fl.num_flags)
|
|
return ui._encode("I", adjusted) .. table.concat(fl.data)
|
|
end
|