231 lines
6.7 KiB
Lua
231 lines
6.7 KiB
Lua
local sharedstate={}
|
|
local logging=require((...):gsub("(.)$", "%1.") .. 'logging')
|
|
|
|
-- function names
|
|
local is_initialized, callback_value, set_value,
|
|
get_value, resolve_key, resolve_index
|
|
|
|
--schema: similar to state_table, nested table only contains value
|
|
local state_queue={}
|
|
|
|
-- we're protecting internal variables here, i'm *that* scared of writing bad
|
|
-- code again.
|
|
-- this section is purely for making sure the tables are in a consistent state
|
|
-- maybe this should be a separate library for general state tracking, then i
|
|
-- could use it in other parts of my code for local state tracking
|
|
-- (aka how to unnecessarily increase complexity, if i want to do something i
|
|
-- can just make a function for it)
|
|
do
|
|
-- schema: {key: {value: val, old_value: val, callback: function,
|
|
-- index: integer}}
|
|
-- wrapping the value in a table should allow the user to store nil
|
|
local state_table={}
|
|
|
|
-- schema: {1: key_name_for_val_1, ...}
|
|
-- this is to save bandwidth in pings
|
|
local state_map={}
|
|
|
|
--- Check if initialized
|
|
---Check if a key is initialized
|
|
---@param key string key
|
|
---@return true if initialized, else false
|
|
function is_initialized(key)
|
|
logging.trace('is_initialized', key)
|
|
return state_table[key] ~= nil
|
|
end
|
|
|
|
|
|
--- Internal; ensure key is initialized
|
|
---Properly initialize a key in all tables.
|
|
---already initialized
|
|
---@param key string key name
|
|
---@return boolean if key has been initialized
|
|
local function init_key(key)
|
|
logging.trace('init_key', key)
|
|
if key == nil then
|
|
local errormsg="sharedstate: A key name is required"
|
|
error(errormsg)
|
|
end
|
|
|
|
if is_initialized(key) then
|
|
return false
|
|
end
|
|
-- lua is fucking asinine and starts tables at 1
|
|
local index=#state_map+1
|
|
state_map[index]=key
|
|
|
|
local tbl={}
|
|
tbl["index"]=index
|
|
state_table[key]=tbl
|
|
return true
|
|
end
|
|
|
|
--- Run callback function
|
|
---Run the callback function of the given key
|
|
---@param key string Key
|
|
function callback_value(key)
|
|
logging.trace("callback_value", key)
|
|
local new_value=state_table[key]["value"]
|
|
local old_value=state_table[key]["old_value"]
|
|
if type(state_table[key]["callback"]) == "function"
|
|
and old_value ~= new_value then
|
|
state_table[key]["callback"](new_value, old_value)
|
|
end
|
|
end
|
|
|
|
--- Set value and run callback
|
|
-- Sets a value in the state table, initializing it if neede, and runs
|
|
-- the callback if it has been previously set. This function should
|
|
-- only be called within ping receiver functions
|
|
---@param key string key
|
|
---@param value any value
|
|
---@param callback? function callback function
|
|
function set_value(key, value, callback)
|
|
logging.trace("set_value", key, value, callback)
|
|
local initialized=init_key(key)
|
|
local entry=state_table[key]
|
|
if initialized then
|
|
entry["value"]=value
|
|
entry["old_value"]=value
|
|
entry["callback"]=callback
|
|
|
|
else
|
|
entry["value"]=value or entry["value"]
|
|
callback_value(key)
|
|
entry["callback"]=callback or entry["callback"]
|
|
entry["old_value"]=value
|
|
end
|
|
end
|
|
|
|
--- Get value
|
|
---Get a value from the state table
|
|
---@param key string key
|
|
---@return any value from state table if set, else nil
|
|
function get_value(key)
|
|
logging.trace("get_value", key)
|
|
if not is_initialized(key) then return nil end
|
|
return state_table[key]["value"]
|
|
end
|
|
|
|
--- Resolve key
|
|
---Resolve key of given index
|
|
---@param index integer index
|
|
---@return any key
|
|
function resolve_key(index)
|
|
logging.trace("resolve_key", index)
|
|
return state_map[index]
|
|
end
|
|
|
|
--- Resolve index
|
|
---Resolve index of given key
|
|
---@param key string key
|
|
---@return integer index
|
|
function resolve_index(key)
|
|
logging.trace("resolve_index", key)
|
|
return state_table[key]["index"]
|
|
end
|
|
end
|
|
|
|
---Add an item to the shared state store.
|
|
---Adds an item to the shared state store, as well as initializes it with a
|
|
---value. This does not send a ping and should only be used during avatar
|
|
---initialization.
|
|
---@param key string key name
|
|
---@param value any initial value
|
|
---@param callback? function Callback function to run on value change
|
|
function sharedstate.init(key, value, callback)
|
|
logging.trace("sharedstate.init", key, value, callback)
|
|
set_value(key, value, callback)
|
|
end
|
|
---@deprecated use sharedstate.init instead
|
|
sharedstate.add=sharedstate.init
|
|
|
|
--- Internal; transfer value over network
|
|
---Ping used to transfer a value over the network, should not be called
|
|
---directly
|
|
---@param index integer Index of key
|
|
---@param value any New value
|
|
function pings.sharedstate_recv(index, value)
|
|
logging.trace("pings.sharedstate_recv", index, value)
|
|
set_value(resolve_key(index), value)
|
|
end
|
|
|
|
--- Internal; transfer value over network using key names
|
|
---Ping used to transfer a value over the network, should not be called
|
|
---directly
|
|
---@param key string Key
|
|
---@param value any New value
|
|
function pings.sharedstate_recv_named(key, value)
|
|
logging.trace("pings.sharedstate_recv_named", key, value)
|
|
set_value(key, value)
|
|
end
|
|
|
|
--- Internal; unpack a table of state into the local state store
|
|
---@param tbl table Table to unpack
|
|
function pings.sharedstate_recv_table(tbl)
|
|
for k, v in pairs(tbl) do
|
|
set_value(k, v["value"])
|
|
end
|
|
end
|
|
|
|
--- Set shared value
|
|
---Sets a shared value. This sends a ping to transfer it over the network.
|
|
---@param key string key name
|
|
---@param value any value
|
|
function sharedstate.set(key, value)
|
|
logging.trace("sharedstate.set", key, value)
|
|
if not is_initialized(key) then
|
|
-- this makes the error traceback less confusing
|
|
local errormsg="sharedstate: Key " .. key .. " has not been initialized."
|
|
error(errormsg)
|
|
end
|
|
pings.sharedstate_recv(resolve_index(key), value)
|
|
-- pings.sharedstate_recv_named(key, value)
|
|
end
|
|
|
|
---Queue entries for sending
|
|
--Queues entries for sending over a single ping, as to prevent being rate
|
|
--limited. Values will not be accessible until sharedstate.commit() is run.
|
|
---@param key string key
|
|
---@param value any value
|
|
function sharedstate.queue(key, value)
|
|
local t={
|
|
["value"] = value
|
|
}
|
|
state_queue[key]=t
|
|
end
|
|
|
|
---Commit queued entries
|
|
--Commit queued table entries and send in one ping.
|
|
function sharedstate.commit()
|
|
--schema: similar to state_table, nested table only contains value
|
|
-- local send_queue={}
|
|
-- -- this is intentional values in state_queued are key names
|
|
-- for _, key in pairs(state_queued) do
|
|
-- local t={
|
|
-- ["value"]=get_value(key)
|
|
-- }
|
|
-- send_queue[key]=t
|
|
-- end
|
|
|
|
pings.sharedstate_recv_table(state_queue)
|
|
sharedstate.clear()
|
|
end
|
|
|
|
--- Clear a value from the shared state queue
|
|
--Clears a value from the shared state queue. If no key is specified, clears
|
|
--all queued values.
|
|
---@param key? string Key to clear
|
|
function sharedstate.clear(key)
|
|
if key ~= nil then
|
|
state_queue[key] = nil
|
|
else
|
|
state_queue={}
|
|
end
|
|
end
|
|
|
|
-- this can be copied directly from the internal functions as it is read only
|
|
sharedstate.get=get_value
|
|
|
|
return sharedstate
|