mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 19:45:22 +09:00
Store Refactor
This commit is contained in:
@@ -1,184 +1,105 @@
|
||||
--- A system which stores peristent data and makes it easy to sync updates between changes
|
||||
|
||||
--[[
|
||||
>>>> What the system is for
|
||||
This module is made so that data can be saved easily in global and have data be synced for the player, force or game.
|
||||
This means that when the value is updated the callback will be rasied with object which it effects eg when set to force
|
||||
type each force will have a stored value and when it changes the force and the new value will be passed to the update callback
|
||||
the same can be done for a surface or players. In a sence this is just an easy way to keep data that can be updated at any time
|
||||
and not have confilcts with other data; for example force settings changed in a gui.
|
||||
|
||||
>>>> How to register a location
|
||||
Every location that is used must be registed with an update_callback which will be called when the value is updated, to do this you
|
||||
must use Store.register_location and pass a string that referses to the location (must be unqiue) for example 'settings.force' might be
|
||||
the location where you store custom settings for a force and 'settings.force.manual_mining_speed_modifier' will be a value for the mining speed.
|
||||
Note: these are not connected directly to the objects you need your own way to update the stored value.
|
||||
|
||||
Continuing with this example you would want to have the store_type as 'force' which will mean that sub_location will refer to different
|
||||
force names, this way each force will have its own store of data all at the same location. The store_type has two speaial cases: local and
|
||||
game (global not yet impimented) where 'local' will not store any data and acts as a reditrect to update_callback and can be used when you
|
||||
dont want any persitent data but still have mutiple places where the value will be updated from. 'game' will have only a single stored value
|
||||
and is because of this will not have a sub_location.
|
||||
|
||||
Finaly the update_callback is the method which you should use to watch for updates to the value from any source, for all types (apart from the
|
||||
2 speaial types listed above) the first param will be the object that it conceners ie the player, force or surface and the second param will be
|
||||
the new value that is set. For game and local there is no sub_location and so there is no object that is passed so the first param is the value
|
||||
that was set. Note that they may be more args after these which were passed from the set_location call these are for your own use.
|
||||
|
||||
Example:
|
||||
|
||||
Store.register_location('settings.force.manual_mining_speed_modifier','force',function(force,value)
|
||||
force.manual_mining_speed_modifier = value
|
||||
end)
|
||||
|
||||
>>>> When should I use a getter
|
||||
Some types of data (such as the mining speed for a force) lend them selfs nicely to using a getter function, where unless a value was set then
|
||||
the getter function will be used to get the value for get_location. This is basicly a way to have a default value for the store when no calls
|
||||
have been made to set a value.
|
||||
|
||||
Note that using a getter function does not mean the store will listen for updates for the returned value of this function.
|
||||
|
||||
Example:
|
||||
|
||||
Store.register_getter('settings.force.manual_mining_speed_modifier',function(force)
|
||||
return force.manual_mining_speed_modifier
|
||||
end)
|
||||
|
||||
>>>> Getting and setting values
|
||||
Once a location is registered you can use the get and set location functions this will allow new values to be set at a location and to retive the current
|
||||
or default (via getter if present).
|
||||
|
||||
Example:
|
||||
|
||||
Store.set_location('settings.force.manual_mining_speed_modifier',game.player.force,5)
|
||||
|
||||
Store.get_location('settings.force.manual_mining_speed_modifier',game.player.force)
|
||||
|
||||
>>>> Functions:
|
||||
Store.register_location(location,store_type,update_callback) --- Registers a new store location
|
||||
Store.register_getter(location,get_callback) --- Registers an optional getter funtion that will return a value when the stored value is nil
|
||||
Store.set_location(location,sub_location,value,...) --- Sets a new value for a location, will trigger the update callback
|
||||
Store.get_location(location,sub_location,allow_invalid_location) --- Gets the value for a location
|
||||
]]
|
||||
|
||||
local Global = require 'utils.global'
|
||||
local Event = require 'utils.event'
|
||||
local Game = require 'utils.game'
|
||||
local Enum = ext_require('expcore.common','enum')
|
||||
|
||||
local Store = {
|
||||
data = {},
|
||||
locations = {},
|
||||
data={},
|
||||
locations={},
|
||||
types = Enum{
|
||||
'local', -- no persistent data, only triggers update_callback
|
||||
'player', -- each player has they own sub_location
|
||||
'force', -- each force has its own sub_location
|
||||
'surface', -- each surface has its own sub_location
|
||||
'game', -- the entrie game has a single store of data
|
||||
'global' -- not yet impimented, data will sync between all servers
|
||||
'local', -- data is not stored with any sub_location, updates caused only by set
|
||||
'player', -- data is stroed per player, updates caused by watch and set
|
||||
'force', -- data is stroed per force, updates caused by watch and set
|
||||
'surface', -- data is stroed per surface, updates caused by watch and set
|
||||
'game', -- data is stored with any sub_location, updates caused by watch and set
|
||||
'global' -- data is stored externaly with any sub_location, updates casued by watch, set and the external source
|
||||
}
|
||||
}
|
||||
Global.register(Store.data,function(tbl)
|
||||
Store.data = tbl
|
||||
Store.data = table
|
||||
end)
|
||||
|
||||
local function get_sub_location(type,sub_location,value)
|
||||
if location.type == Store.types['local'] then
|
||||
return nil,sub_location
|
||||
elseif location.type == Store.types.player then
|
||||
local function get_sub_location_object(store_type,sub_location)
|
||||
if store_type == Store.types.player then
|
||||
sub_location = Game.get_player_from_any(sub_location)
|
||||
if not sub_location then return error('Invalid player for sub_location',3) end
|
||||
return sub_location,value
|
||||
elseif location.type == Store.types.force then
|
||||
return sub_location
|
||||
elseif store_type == Store.types.force then
|
||||
sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.forces[sub_location]
|
||||
if not sub_location then return error('Invalid force for sub_location',3) end
|
||||
return sub_location,value
|
||||
elseif location.type == Store.types.surface then
|
||||
return sub_location
|
||||
elseif store_type == Store.types.surface then
|
||||
sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.surfaces[sub_location]
|
||||
if not sub_location then return error('Invalid surface for sub_location',3) end
|
||||
return sub_location,value
|
||||
elseif location.type == Store.types.game then
|
||||
return nil,sub_location
|
||||
elseif location.type == Store.types.glboal then
|
||||
return nil,sub_location
|
||||
return sub_location
|
||||
end
|
||||
end
|
||||
|
||||
--- Registers a new store location
|
||||
-- @tparam location string a unique location string that will hold the data
|
||||
-- @tparam type string see Store.types
|
||||
-- @tparam update_callback function the function which will be called with the new value that is set
|
||||
function Store.register_location(location,store_type,update_callback)
|
||||
if Store.locations[location] then
|
||||
store_type = Store.locations[location]
|
||||
store_type = type(store_type) == 'number' and Store.types(store_type) or store_type
|
||||
return error('The location is already registed: '..location..' and is type: '..store_type,2)
|
||||
local function set_global_location_value(location,sub_location,value)
|
||||
-- not yet impimented, this will emit to a file in some way to set the value in an external database
|
||||
end
|
||||
|
||||
function Store.register(location,store_type,getter,setter,no_error)
|
||||
if not no_error and Store.locations[location] then
|
||||
return error('The location is already registed: '..location,2)
|
||||
end
|
||||
if not Store.type[type] then
|
||||
return error('Attempted to set invlid type: '..type..' for location: '..location,2)
|
||||
end
|
||||
store_type = type(store_type) == 'string' and Store.types(store_type) or store_type
|
||||
store_type = type(store_type) == 'string' and Store.types[store_type] or store_type
|
||||
Store.locations[location] = {
|
||||
location=location,
|
||||
type=store_type,
|
||||
update_callback=update_callback
|
||||
store_type=store_type,
|
||||
getter=getter,
|
||||
setter=setter
|
||||
}
|
||||
if store_type ~= Store.types['local'] and store_type ~= Store.types.global then
|
||||
Store.data[location] = {}
|
||||
end
|
||||
end
|
||||
|
||||
--- Registers an optional getter funtion that will return a value when the stored value is nil
|
||||
-- @tparam location string the location to set the data at, must be registed
|
||||
-- @tparam get_callback function the function that will be called to return the value
|
||||
function Store.register_getter(location,get_callback)
|
||||
function Store.set(location,sub_location,value)
|
||||
if not Store.locations[location] then
|
||||
return error('Invalid store location: '..location,2)
|
||||
return error('The location is not registed: '..location)
|
||||
end
|
||||
location = Store.locations[location]
|
||||
location.get_callback = get_callback
|
||||
local sub_location_object = get_sub_location_object(location.store_type,sub_location)
|
||||
if location.store_type ~= Store.types['local'] then
|
||||
if not Store.data[location.location] then Store.data[location.location] = {} end
|
||||
Store.data[location.location][sub_location] = value
|
||||
end
|
||||
if location.store_type == Store.types.global then
|
||||
set_global_location_value(location.location,value)
|
||||
end
|
||||
location.setter(sub_location_object or sub_location,value)
|
||||
end
|
||||
|
||||
--- Sets a new value for a location, will trigger the update callback
|
||||
-- @tparam location string the location to set the data at, must be registed
|
||||
-- @tparam[opt] sub_location string a second location value that can be a player's name force name etc
|
||||
-- @tparam value any the value to be stored, passed via sublocation if sub_location is not required
|
||||
-- @tparam[opt] ... any any more values that you want to pass to the update callback
|
||||
function Store.set_location(location,sub_location,value,...)
|
||||
if not Store.locations[location] then
|
||||
return error('Invalid store location: '..location,2)
|
||||
end
|
||||
function Store.get(location,sub_location)
|
||||
if not Store.locations[location] then return end
|
||||
location = Store.locations[location]
|
||||
local _sub_location,_value = get_sub_location(type,sub_location,value)
|
||||
if _sub_location then
|
||||
Store.data[location][_sub_location] = _value
|
||||
location.update_callback(_sub_location,_value,...)
|
||||
else
|
||||
Store.data[location] = _value
|
||||
location.update_callback(_value,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the value for a location
|
||||
-- @tparam location string the location to set the data at, must be registed
|
||||
-- @tparam[opt] sub_location string a second location value that can be a player's name force name etc
|
||||
-- @tparam[opt=false] allow_invalid_location boolean when true will not error when location is invalid
|
||||
-- @treturn any the value found at that location
|
||||
function Store.get_location(location,sub_location,allow_invalid_location)
|
||||
if not Store.locations[location] then
|
||||
return not allow_invalid_location and error('Invalid store location: '..location,2) or nil
|
||||
end
|
||||
location = Store.locations[location]
|
||||
local rtn
|
||||
if location.type == Store.types.game then
|
||||
rtn = Store.data[location.location]
|
||||
elseif location.type ~= Store.types['local'] and location.type ~= Store.types.global then
|
||||
rtn = Store.data[location.location][sub_location]
|
||||
end
|
||||
if rtn == nil and location.get_callback then
|
||||
sub_location = get_sub_location(location.type,sub_location)
|
||||
rtn = location.get_callback(sub_location)
|
||||
end
|
||||
local sub_location_object = get_sub_location_object(location.store_type,sub_location)
|
||||
local rtn = Store.data[location.location][sub_location]
|
||||
if rtn == nil then rtn = location.getter(sub_location_object or sub_location) end
|
||||
return rtn
|
||||
end
|
||||
|
||||
function Store.check(location,sub_location)
|
||||
if not Store.locations[location] then return false end
|
||||
location = Store.locations[location]
|
||||
local sub_location_object = get_sub_location_object(location.store_type,sub_location)
|
||||
local store,getter = Store.data[location.location][sub_location],location.getter(sub_location_object or sub_location)
|
||||
if store ~= getter then
|
||||
if not Store.data[location.location] then Store.data[location.location] = {} end
|
||||
Store.data[location.location][sub_location] = getter
|
||||
location.setter(sub_location_object or sub_location,getter)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
Event.on_nth_tick(60,function()
|
||||
for _,location in pairs(Store.locations) do
|
||||
if location.store_type ~= Store.types['local'] then
|
||||
if not Store.data[location.location] then Store.data[location.location] = {} end
|
||||
for sub_location,_ in pairs(Store.data[location.location]) do
|
||||
Store.check(location,sub_location)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return Store
|
||||
Reference in New Issue
Block a user