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