From 18046a9fccbdeab85ee6c22354c2669cfcc6780d Mon Sep 17 00:00:00 2001 From: NullBite Date: Thu, 22 Jun 2023 19:37:54 -0400 Subject: [PATCH] Add sharedstate.lua Library for tracking shared variables via pings --- sharedstate.lua | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 sharedstate.lua diff --git a/sharedstate.lua b/sharedstate.lua new file mode 100644 index 0000000..6555d6e --- /dev/null +++ b/sharedstate.lua @@ -0,0 +1,157 @@ +local sharedstate={} +logging=require((...):gsub("(.)$", "%1.") .. 'logging') + +-- 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 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 key name + --@return true if key has been initialized, false if exists + local function init_key(key) + logging.trace('init_key', key) + if key == nil then + error("sharedstate: A key name is required") + 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 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 + -- @param key key + -- @param value value + -- @param callback 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 key + --@return 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 index + --@return key + function resolve_key(index) + logging.trace("resolve_key", index) + return state_map[index] + end + + --- Resolve index + --Resolve index of given key + --@param key key + --@return 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 key name +--@param value initial value +--@param callback Callback function to run on value change +function sharedstate.add(key, value, callback) + logging.trace("sharedstate.add", key, value, callback) + set_value(key, value, callback) +end + +--- Internal; transfer value over network +-- Ping used to transfer a value over the network, should not be called +-- directly +-- @param index Index of key +-- @param value New value +function pings.sharedstate_transfer(index, value) + logging.trace("pings.sharedstate_transfer", index, value) + set_value(resolve_key(index), value) +end + +--- Set shared value +-- Sets a shared value. This sends a ping to transfer it over the network. +-- @param key key name +-- @param value value +function sharedstate.set(key, value) + logging.trace("sharedstate.set", key, value) + if not is_initialized(key) then + error("sharedstate: Key " .. key .. " has not been initialized.") + end + pings.sharedstate_transfer(resolve_index(key), value) +end + +-- this can be copied directly from the internal functions as it is read only +sharedstate.get=get_value + +return sharedstate