mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 11:35:22 +09:00
Added doc comments
This commit is contained in:
@@ -1,3 +1,150 @@
|
||||
--[[-- Core Module - Datastore
|
||||
- A module used to store data in the global table with the option to have it sync to an external source.
|
||||
@core Datastore
|
||||
@alias DatastoreManager
|
||||
|
||||
@usage-- Types of Datastore
|
||||
-- This datastore will not save data externally and can be used to watch for updates on values within it
|
||||
-- A common use might be to store data for a gui and only update the gui when a value changes
|
||||
local LocalDatastore = Datastore.connect('LocalDatastore')
|
||||
|
||||
-- This datastore will allow you to use the save and request method, this allows you to have persistent data
|
||||
-- Should be used over auto save as it creates less save requests, but this means you need to tell the data to be saved
|
||||
-- We use this type for player data as we know the data only needs to be saved when the player leaves
|
||||
local PersistentDatastore = Datastore.connect('PersistentDatastore', true) -- save_to_disk
|
||||
|
||||
-- This datastore is the same as above but the save method will be called automatically when ever you change a value
|
||||
-- An auto save datastore should be used if the data does not change often, this can be global settings and things of that sort
|
||||
-- If it is at all possible to setup events to unload and/or save the data then this is preferable
|
||||
local AutosaveDatastore = Datastore.connect('AutosaveDatastore', true, true) -- save_to_disk, auto_save
|
||||
|
||||
-- Finally you can have a datastore that propagates its changes to all other connected servers, this means request does not need to be used
|
||||
-- This should be used when you might have data conflicts while saving, this is done by pushing the saved value to all active servers
|
||||
-- The request method has little use after server start as any external changes to the value will be pushed automatically
|
||||
-- Auto save can also be used with this type and you should follow the same guidelines above for when this should be avoided
|
||||
local PropagateDatastore = Datastore.connect('PropagateDatastore', true, false, true) -- save_to_disk, propagate_changes
|
||||
|
||||
@usage-- Using Datastores Locally
|
||||
-- Once you have your datastore connection setup, any further requests with connect will return the same datastore
|
||||
-- This is important to know because the settings passed as parameters you have an effect when it is first created
|
||||
|
||||
-- One useful thing that you might want to set up before runtime is a serializer, this will convert non string keys into strings
|
||||
-- This serializer will allow use to pass a player object and still have it serialized to the players name
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:set_serializer(function(rawKey)
|
||||
return rawKey.name
|
||||
end)
|
||||
|
||||
-- If we want to get data from the datastore we can use get or get_all
|
||||
local value = ExampleData:get(player, defaultValue)
|
||||
local values = ExampleData:get_all()
|
||||
|
||||
-- If we want to set data then we can use set, increment, update, or update_all
|
||||
ExampleData:set(player, 10)
|
||||
ExampleData:increment(player)
|
||||
ExampleData:update(player, function(player_name, value)
|
||||
return value * 2
|
||||
end)
|
||||
ExampleData:update_all(function(player_name, value)
|
||||
return value * 2
|
||||
end)
|
||||
|
||||
-- If we want to remove data then we use remove
|
||||
ExampleData:remove(player)
|
||||
|
||||
-- We can also listen for updates to a value done by any of the above methods with on_update
|
||||
ExampleData:on_update(function(player_name, value)
|
||||
game.print(player_name..' has had their example data updated to '..tostring(value))
|
||||
end)
|
||||
|
||||
@usage-- Using Datastore Externally
|
||||
-- If save_to_disk is used then this opens up the option for persistent data which you can request, save, and remove
|
||||
-- All of the local methods are still usable put now there is the option for extra events
|
||||
-- In order for this to work there must be an external script to read datastore.pipe and inject with Datastore.ingest
|
||||
|
||||
-- To request data you would use request and the on_load event, this event can be used to modify data before it is used
|
||||
ExampleData:request(player)
|
||||
ExampleData:on_load(function(player_name, value)
|
||||
game.print('Loaded example data for '..player_name)
|
||||
-- A value can be returned here to overwrite the received value
|
||||
end)
|
||||
|
||||
-- To save data you would use save and the on_save event, this event can be used to modify data before it is saved
|
||||
ExampleData:save(player)
|
||||
ExampleData:on_save(function(player_name, value)
|
||||
game.print('Saved example data for '..player_name)
|
||||
-- A value can be returned here to overwrite the value which is saved
|
||||
end)
|
||||
|
||||
-- To remove data locally but not externally, like if a player logs off, you would use unload and on_unload
|
||||
ExampleData:unload(player)
|
||||
ExampleData:on_unload(function(player_name, value)
|
||||
game.print('Unloaded example data for '..player_name)
|
||||
-- Any return is ignored, this is event is for cleaning up other data
|
||||
end)
|
||||
|
||||
@usage-- Using Datastore Messaging
|
||||
-- The message action can be used regardless of save_to_disk being set as no data is saved, but an external script is still required
|
||||
-- These messages can be used to send data to other servers which doesnt need to be saved such as shouts or commands
|
||||
-- Using messages is quite simple only using message and on_message
|
||||
ExampleData:message(key, message)
|
||||
ExampleData:on_message(function(key, message)
|
||||
game.print('Received message '..message)
|
||||
end)
|
||||
|
||||
@usage-- Combined Datastores
|
||||
-- A combined datastore is a datastore which stores its data inside of another datastore
|
||||
-- This means that the data is stored more efficiently in the external database and less requests need to be made
|
||||
-- To understand how combined datastores work think of each key in the parent as a table where the sub datastore is a key in that table
|
||||
-- Player data is the most used version of the combined datastore, below is how the player data module is setup
|
||||
local PlayerData = Datastore.connect('PlayerData', true) -- saveToDisk
|
||||
PlayerData:set_serializer(Datastore.name_serializer) -- use player name as key
|
||||
PlayerData:combine('Statistics')
|
||||
PlayerData:combine('Settings')
|
||||
PlayerData:combine('Required')
|
||||
|
||||
-- You can then further combine datastores to any depth, below we add some possible settings and statistics that we might use
|
||||
-- Although we dont in this example, each of these functions returns the datastore object which you should use as a local value
|
||||
PlayerData.Settings:combine('Color')
|
||||
PlayerData.Settings:combine('Quickbar')
|
||||
PlayerData.Settings:combine('JoinMessage')
|
||||
PlayerData.Statistics:combine('Playtime')
|
||||
PlayerData.Statistics:combine('JoinCount')
|
||||
|
||||
-- Because sub datastore work just like a normal datastore you dont need any special code, using get and set will still return as if it wasnt a sub datastore
|
||||
-- Things like the serializer and the datastore settings are always the same as the parent so you dont need to worry about setting up the serializer each time
|
||||
-- And because save, request, and unload methods all point to the root datastore you are able to request and save your data as normal
|
||||
|
||||
-- If you used get_all on PlayerData this is what you would get:
|
||||
{
|
||||
Cooldude2606 = {
|
||||
Settings = {
|
||||
Color = 'ColorValue',
|
||||
Quickbar = 'QuickbarValue',
|
||||
JoinMessage = 'JoinMessageValue'
|
||||
},
|
||||
Statistics = {
|
||||
Playtime = 'PlaytimeValue',
|
||||
JoinCount = 'JoinCountValue'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- If you used get_all on PlayerData.Settings this is what you would get:
|
||||
{
|
||||
Cooldude2606 = {
|
||||
Color = 'ColorValue',
|
||||
Quickbar = 'QuickbarValue',
|
||||
JoinMessage = 'JoinMessageValue'
|
||||
}
|
||||
}
|
||||
|
||||
-- If you used get_all on PlayerData.Settings.Color this is what you would get:
|
||||
{
|
||||
Cooldude2606 = 'ColorValue'
|
||||
}
|
||||
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
|
||||
@@ -11,8 +158,8 @@ local copy = table.deep_copy
|
||||
global.datastores = Data
|
||||
Event.on_load(function()
|
||||
Data = global.datastores
|
||||
for tableName, datastore in pairs(Datastores) do
|
||||
datastore.data = Data[tableName]
|
||||
for datastoreName, datastore in pairs(Datastores) do
|
||||
datastore.data = Data[datastoreName]
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -26,17 +173,27 @@ DatastoreManager.metatable = {
|
||||
__call = function(self, ...) return self:get(...) end
|
||||
}
|
||||
|
||||
--- Make a new datastore connection, if a connection already exists then it is returned
|
||||
function DatastoreManager.connect(tableName, saveToDisk, autoSave, propagateChanges)
|
||||
if Datastores[tableName] then return Datastores[tableName] end
|
||||
--[[-- Make a new datastore connection, if a connection already exists then it is returned
|
||||
@tparam string datastoreName The name that you want the new datastore to have, this can not have any whitespace
|
||||
@tparam[opt=false] boolean saveToDisk When set to true, using the save method with write the data to datastore.pipe
|
||||
@tparam[opt=false] boolean autoSave When set to true, using any method which modifies data will cause the data to be saved
|
||||
@tparam[opt=false] boolean propagateChanges When set to true, using the save method will send the data to all other connected servers
|
||||
@treturn table The new datastore connection that can be used to access and modify data in the datastore
|
||||
|
||||
@usage-- Connecting to the test datastore which will allow saving to disk
|
||||
local ExampleData = Datastore.connect('ExampleData', true) -- saveToDisk
|
||||
|
||||
]]
|
||||
function DatastoreManager.connect(datastoreName, saveToDisk, autoSave, propagateChanges)
|
||||
if Datastores[datastoreName] then return Datastores[datastoreName] end
|
||||
if _LIFECYCLE ~= _STAGE.control then
|
||||
-- Only allow this function to be called during the control stage
|
||||
error('New datastore connection can not be created during runtime', 2)
|
||||
end
|
||||
|
||||
local new_datastore = {
|
||||
name = tableName,
|
||||
table_name = tableName,
|
||||
name = datastoreName,
|
||||
value_name = datastoreName,
|
||||
auto_save = autoSave or false,
|
||||
save_to_disk = saveToDisk or false,
|
||||
propagate_changes = propagateChanges or false,
|
||||
@@ -48,28 +205,38 @@ function DatastoreManager.connect(tableName, saveToDisk, autoSave, propagateChan
|
||||
data = {}
|
||||
}
|
||||
|
||||
Data[tableName] = new_datastore.data
|
||||
Datastores[tableName] = new_datastore
|
||||
Data[datastoreName] = new_datastore.data
|
||||
Datastores[datastoreName] = new_datastore
|
||||
return setmetatable(new_datastore, DatastoreManager.metatable)
|
||||
end
|
||||
|
||||
--- Make a new datastore that stores its data inside of another one
|
||||
function DatastoreManager.combine(datastore, subTableName)
|
||||
local new_datastore = DatastoreManager.connect(datastore.name..'.'..subTableName)
|
||||
datastore.children[subTableName] = new_datastore
|
||||
new_datastore.serializer = datastore.serializer
|
||||
new_datastore.auto_save = datastore.auto_save
|
||||
new_datastore.table_name = subTableName
|
||||
new_datastore.parent = datastore
|
||||
Data[new_datastore.name] = nil
|
||||
new_datastore.data = nil
|
||||
return new_datastore
|
||||
--[[-- Make a new datastore that stores its data inside of another one
|
||||
@tparam string datastoreName The name of the datastore that will contain the data for the new datastore
|
||||
@tparam string subDatastoreName The name of the new datastore, this name will also be used as the key inside the parent datastore
|
||||
@treturn table The new datastore connection that can be used to access and modify data in the datastore
|
||||
|
||||
@usage-- Setting up a datastore which stores its data inside of another datastore
|
||||
local BarData = Datastore.combine('ExampleData', 'Bar')
|
||||
|
||||
]]
|
||||
function DatastoreManager.combine(datastoreName, subDatastoreName)
|
||||
local datastore = assert(Datastores[datastoreName], 'Datastore not found '..tostring(datastoreName))
|
||||
return datastore:combine(subDatastoreName)
|
||||
end
|
||||
|
||||
--- Ingest the result from a request, this is used through a rcon interface to sync data
|
||||
--[[-- Ingest the result from a request, this is used through a rcon interface to sync data
|
||||
@tparam string action The action that should be done, can be: remove, message, propagate, or request
|
||||
@tparam string datastoreName The name of the datastore that should have the action done to it
|
||||
@tparam string key The key of that datastore that is having the action done to it
|
||||
@tparam string valueJson The json string for the value being ingested, remove does not require a value
|
||||
|
||||
@usage-- Replying to a data request
|
||||
Datastore.ingest('request', 'ExampleData', 'TestKey', 'Foo')
|
||||
|
||||
]]
|
||||
local function ingest_error(err) print('Datastore ingest error, Unable to parse json:', err) end
|
||||
function DatastoreManager.ingest(action, tableName, key, valueJson)
|
||||
local datastore = assert(Datastores[tableName], 'Datastore ingest error, Datastore not found '..tostring(tableName))
|
||||
function DatastoreManager.ingest(action, datastoreName, key, valueJson)
|
||||
local datastore = assert(Datastores[datastoreName], 'Datastore ingest error, Datastore not found '..tostring(datastoreName))
|
||||
assert(type(action) == 'string', 'Datastore ingest error, Action is not a string got: '..type(action))
|
||||
assert(type(key) == 'string', 'Datastore ingest error, Key is not a string got: '..type(key))
|
||||
|
||||
@@ -78,12 +245,14 @@ function DatastoreManager.ingest(action, tableName, key, valueJson)
|
||||
|
||||
elseif action == 'message' then
|
||||
local success, value = xpcall(game.json_to_table, ingest_error, valueJson)
|
||||
if not success or value == nil then return end
|
||||
if not success then return end
|
||||
if value == nil then value = valueJson end
|
||||
datastore:raise_event('on_message', key, value)
|
||||
|
||||
elseif action == 'propagate' then
|
||||
elseif action == 'propagate' or action == 'request' then
|
||||
local success, value = xpcall(game.json_to_table, ingest_error, valueJson)
|
||||
if not success or value == nil then return end
|
||||
if not success then return end
|
||||
if value == nil then value = valueJson end
|
||||
value = datastore:raise_event('on_load', key, value)
|
||||
datastore:set(key, value)
|
||||
|
||||
@@ -91,22 +260,46 @@ function DatastoreManager.ingest(action, tableName, key, valueJson)
|
||||
|
||||
end
|
||||
|
||||
--- Debug, Use to get all datastores, or return debug info on a datastore
|
||||
function DatastoreManager.debug(tableName)
|
||||
if not tableName then return Datastores end
|
||||
local datastore = assert(Datastores[tableName], 'Datastore not found '..tostring(tableName))
|
||||
--[[-- Debug, Use to get all datastores, or return debug info on a datastore
|
||||
@tparam[opt] string datastoreName The name of the datastore to get the debug info of
|
||||
|
||||
@usage-- Get all the datastores
|
||||
local datastores = Datastore.debug()
|
||||
|
||||
@usage-- Getting the debug info for a datastore
|
||||
local debug_info = Datastore.debug('ExampleData')
|
||||
|
||||
]]
|
||||
function DatastoreManager.debug(datastoreName)
|
||||
if not datastoreName then return Datastores end
|
||||
local datastore = assert(Datastores[datastoreName], 'Datastore not found '..tostring(datastoreName))
|
||||
return datastore:debug()
|
||||
end
|
||||
|
||||
--- Commonly used serializer, returns the name of the object
|
||||
--[[-- Commonly used serializer, returns the name of the object
|
||||
@tparam any rawKey The raw key that will be serialized, this can be things like player, force, surface, etc
|
||||
@treturn string The name of the object that was passed
|
||||
|
||||
@usage-- Using the name serializer for your datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:set_serializer(Datastore.name_serializer)
|
||||
|
||||
]]
|
||||
function DatastoreManager.name_serializer(rawKey)
|
||||
return rawKey.name
|
||||
end
|
||||
|
||||
----- Datastore -----
|
||||
-- @section datastore
|
||||
----- Datastore Internal -----
|
||||
-- @section datastore-internal
|
||||
|
||||
--- Debug, Get the debug info for this datastore
|
||||
--[[-- Debug, Get the debug info for this datastore
|
||||
@treturn table The debug info for this datastore, contains stuff like parent, settings, children, etc
|
||||
|
||||
@usage-- Get the debug info for a datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
local debug_info = ExampleData:debug()
|
||||
|
||||
]]
|
||||
function Datastore:debug()
|
||||
local debug_info = {}
|
||||
|
||||
@@ -130,12 +323,20 @@ function Datastore:debug()
|
||||
return debug_info
|
||||
end
|
||||
|
||||
--- Internal, Get data following combine logic
|
||||
--[[-- Internal, Get data following combine logic
|
||||
@tparam string key The key to get the value of from this datastore
|
||||
@tparam[opt=false] boolean fromChild If the get request came from a child of this datastore
|
||||
@treturn any The value that was stored at this key in this datastore
|
||||
|
||||
@usage-- Internal, Get the data from a datastore
|
||||
local value = self:raw_get('TestKey')
|
||||
|
||||
]]
|
||||
function Datastore:raw_get(key, fromChild)
|
||||
local data = self.data
|
||||
if self.parent then
|
||||
data = self.parent:raw_get(key, true)
|
||||
key = self.table_name
|
||||
key = self.value_name
|
||||
end
|
||||
local value = data[key]
|
||||
if value ~= nil then return value end
|
||||
@@ -144,18 +345,32 @@ function Datastore:raw_get(key, fromChild)
|
||||
return value
|
||||
end
|
||||
|
||||
--- Internal, Set data following combine logic
|
||||
--[[-- Internal, Set data following combine logic
|
||||
@tparam string key The key to set the value of in this datastore
|
||||
@tparam any value The value that will be set at this key
|
||||
|
||||
@usage-- Internal, Set the value in a datastore
|
||||
self:raw_set('TestKey', 'Foo')
|
||||
|
||||
]]
|
||||
function Datastore:raw_set(key, value)
|
||||
if self.parent then
|
||||
local data = self.parent:raw_get(key, true)
|
||||
data[self.table_name] = value
|
||||
data[self.value_name] = value
|
||||
else
|
||||
self.data[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
--- Internal, Return the serialized key
|
||||
local function serialize_error(err) error('An error ocurred in a datastore serializer: '..err) end
|
||||
--[[-- Internal, Return the serialized key
|
||||
@tparam any rawKey The key that needs to be serialized, if it is already a string then it is returned
|
||||
@treturn string The key after it has been serialized
|
||||
|
||||
@usage-- Internal, Ensure that the key is a string
|
||||
key = self:serialize(key)
|
||||
|
||||
]]
|
||||
function Datastore:serialize(rawKey)
|
||||
if type(rawKey) == 'string' then return rawKey end
|
||||
assert(self.serializer, 'Datastore does not have a serializer and received non string key')
|
||||
@@ -163,7 +378,18 @@ function Datastore:serialize(rawKey)
|
||||
return success and key or nil
|
||||
end
|
||||
|
||||
--- Internal, Writes an event to the output file to be saved and/or propagated
|
||||
--[[-- Internal, Writes an event to the output file to be saved and/or propagated
|
||||
@tparam string action The action that should be wrote to datastore.pipe, can be request, remove, message, save, propagate
|
||||
@tparam string key The key that the action is being preformed on
|
||||
@tparam any value The value that should be used with the action
|
||||
|
||||
@usage-- Write a data request to datastore.pipe
|
||||
self:write_action('request', 'TestKey')
|
||||
|
||||
@usage-- Write a data save to datastore.pipe
|
||||
self:write_action('save', 'TestKey', 'Foo')
|
||||
|
||||
]]
|
||||
function Datastore:write_action(action, key, value)
|
||||
local data = {action, self.name, '"'..key..'"'}
|
||||
if value ~= nil then
|
||||
@@ -172,13 +398,57 @@ function Datastore:write_action(action, key, value)
|
||||
game.write_file('datastore.pipe', table.concat(data, ' ')..'\n', true, 0)
|
||||
end
|
||||
|
||||
--- Set a callback that will be used to serialize keys which aren't strings
|
||||
----- Datastore -----
|
||||
-- @section datastore
|
||||
|
||||
--[[-- Create a new datastore which is stores its data inside of this datastore
|
||||
@tparam string subDatastoreName The name of the datastore that will have its data stored in this datastore
|
||||
@treturn table The new datastore that was created inside of this datastore
|
||||
|
||||
@usage-- Add a new sub datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
local BarData = ExampleData:combine('Bar')
|
||||
|
||||
]]
|
||||
function Datastore:combine(subDatastoreName)
|
||||
local new_datastore = DatastoreManager.connect(self.name..'.'..subDatastoreName)
|
||||
self.children[subDatastoreName] = new_datastore
|
||||
new_datastore.value_name = subDatastoreName
|
||||
new_datastore.serializer = self.serializer
|
||||
new_datastore.auto_save = self.auto_save
|
||||
new_datastore.parent = self
|
||||
Data[new_datastore.name] = nil
|
||||
new_datastore.data = nil
|
||||
return new_datastore
|
||||
end
|
||||
|
||||
--[[-- Set a callback that will be used to serialize keys which aren't strings
|
||||
@tparam function callback The function that will be used to serialize non string keys passed as an argument
|
||||
|
||||
@usage-- Set a custom serializer, this would be the same as Datastore.name_serializer
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:set_serializer(function(rawKey)
|
||||
return rawKey.name
|
||||
end)
|
||||
|
||||
]]
|
||||
function Datastore:set_serializer(callback)
|
||||
assert(type(callback) == 'function', 'Callback must be a function')
|
||||
self.serializer = callback
|
||||
end
|
||||
|
||||
--- Set metadata tags on this datastore which can be accessed by other scripts
|
||||
--[[-- Set metadata tags on this datastore which can be accessed by other scripts
|
||||
@tparam table tags A table of tags that you want to set in the metadata for this datastore
|
||||
|
||||
@usage-- Adding metadata that could be used by a gui to help understand the stored data
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:set_metadata{
|
||||
caption = 'Test Data',
|
||||
tooltip = 'Data used for testing datastores',
|
||||
type = 'table'
|
||||
}
|
||||
|
||||
]]
|
||||
function Datastore:set_metadata(tags)
|
||||
local metadata = self.metadata
|
||||
for key, value in pairs(tags) do
|
||||
@@ -186,50 +456,15 @@ function Datastore:set_metadata(tags)
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new datastore which is stores its data inside of this datastore
|
||||
Datastore.combine = DatastoreManager.combine
|
||||
--[[-- Get a value from local storage, option to have a default value
|
||||
@tparam any key The key that you want to get the value of, must be a string unless a serializer is set
|
||||
@tparam[opt] any default The default value that will be returned if no value is found in the datastore
|
||||
|
||||
--- Request a value from an external source, will trigger on_load when data is received
|
||||
function Datastore:request(key)
|
||||
if self.parent then return self.parent:request(key) end
|
||||
key = self:serialize(key)
|
||||
self:write_action('request', key)
|
||||
end
|
||||
@usage-- Get a key from the datastore, the default will be deep copied if no value exists in the datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
local value = ExampleData:get('TestKey')
|
||||
|
||||
--- Save a value to an external source, will trigger on_save before data is saved, save_to_disk must be set to true
|
||||
function Datastore:save(key)
|
||||
if self.parent then self.parent:save(key) end
|
||||
if not self.save_to_disk then return end
|
||||
key = self:serialize(key)
|
||||
local value = self:raise_event('on_save', key, copy(self:raw_get(key)))
|
||||
local action = self.propagate_changes and 'propagate' or 'save'
|
||||
self:write_action(action, key, value)
|
||||
end
|
||||
|
||||
--- Save a value to an external source and remove locally, will trigger on_unload then on_save, save_to_disk is not required for on_unload
|
||||
function Datastore:unload(key)
|
||||
if self.parent then return self.parent:unload(key) end
|
||||
key = self:serialize(key)
|
||||
self:raise_event('on_unload', key, copy(self:raw_get(key)))
|
||||
self:save(key)
|
||||
self:raw_set(key)
|
||||
end
|
||||
|
||||
--- Use to send a message over the connection, works regardless of saveToDisk and propagateChanges
|
||||
function Datastore:message(key, message)
|
||||
key = self:serialize(key)
|
||||
self:write_action('message', key, message)
|
||||
end
|
||||
|
||||
--- Remove a value locally and on the external source, works regardless of propagateChanges
|
||||
function Datastore:remove(key)
|
||||
key = self:serialize(key)
|
||||
self:raw_set(key)
|
||||
self:write_action('remove', key)
|
||||
if self.parent and self.parent.auto_save then return self.parent:save(key) end
|
||||
end
|
||||
|
||||
--- Get a value from local storage, option to have a default value
|
||||
]]
|
||||
function Datastore:get(key, default)
|
||||
key = self:serialize(key)
|
||||
local value = self:raw_get(key)
|
||||
@@ -237,7 +472,15 @@ function Datastore:get(key, default)
|
||||
return copy(default)
|
||||
end
|
||||
|
||||
--- Set a value in local storage, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||
--[[-- Set a value in local storage, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||
@tparam any key The key that you want to set the value of, must be a string unless a serializer is set
|
||||
@tparam any value The value that you want to set for this key
|
||||
|
||||
@usage-- Set a value in the datastore, this will trigger on_update, if auto_save is true then will trigger save
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:set('TestKey', 'Foo')
|
||||
|
||||
]]
|
||||
function Datastore:set(key, value)
|
||||
key = self:serialize(key)
|
||||
self:raw_set(key, value)
|
||||
@@ -246,15 +489,33 @@ function Datastore:set(key, value)
|
||||
return value
|
||||
end
|
||||
|
||||
--- Increment the value in local storage, only works for number values, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||
--[[-- Increment the value in local storage, only works for number values, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||
@tparam any key The key that you want to increment the value of, must be a string unless a serializer is set
|
||||
@tparam[opt=1] number delta The amount that you want to increment the value by, can be negative or a decimal
|
||||
|
||||
@usage-- Increment a value in a datastore, the value must be a number or nil, if nil 0 is used as the start value
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:increment('TestNumber')
|
||||
|
||||
]]
|
||||
function Datastore:increment(key, delta)
|
||||
key = self:serialize(key)
|
||||
local value = self:raw_get(key) or 0
|
||||
return Datastore:set(key, value + (delta or 1))
|
||||
end
|
||||
|
||||
--- Use a function to update the value locally, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||
local function update_error(err) error('An error ocurred in datastore update: '..err, 2) end
|
||||
--[[-- Use a function to update the value locally, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||
@tparam any key The key that you want to apply the update to, must be a string unless a serializer is set
|
||||
@tparam function callback The function that will be used to update the value at this key
|
||||
|
||||
@usage-- Using a function to update a value, if a value is returned then this will be the new value
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:increment('TestKey', function(key, value)
|
||||
return value..value
|
||||
end)
|
||||
|
||||
]]
|
||||
function Datastore:update(key, callback)
|
||||
key = self:serialize(key)
|
||||
local value = self:raw_get(key)
|
||||
@@ -267,8 +528,33 @@ function Datastore:update(key, callback)
|
||||
end
|
||||
end
|
||||
|
||||
--- Internal, Used to filter elements from a table
|
||||
--[[-- Remove a value locally and on the external source, works regardless of propagateChanges
|
||||
@tparam any key The key that you want to remove locally and externally, must be a string unless a serializer is set
|
||||
|
||||
@usage-- Remove a key locally and externally
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:remove('TestKey')
|
||||
|
||||
]]
|
||||
function Datastore:remove(key)
|
||||
key = self:serialize(key)
|
||||
self:raw_set(key)
|
||||
self:write_action('remove', key)
|
||||
if self.parent and self.parent.auto_save then return self.parent:save(key) end
|
||||
end
|
||||
|
||||
local function filter_error(err) print('An error ocurred in a datastore filter:', err) end
|
||||
--[[-- Internal, Used to filter elements from a table
|
||||
@tparam table tbl The table that will have the filter applied to it
|
||||
@tparam[opt] function callback The function that will be used as a filter, if none giving then the provided table is returned
|
||||
@treturn table The table which has only the key values pairs which passed the filter
|
||||
|
||||
@usage-- Internal, Filter a table by the values it contains, return true to keep the key value pair
|
||||
local filtered_table = filter({5,3,4,1,2}, function(key, value)
|
||||
return value > 2
|
||||
end)
|
||||
|
||||
]]
|
||||
local function filter(tbl, callback)
|
||||
if not callback then return tbl end
|
||||
local rtn = {}
|
||||
@@ -279,26 +565,153 @@ local function filter(tbl, callback)
|
||||
return rtn
|
||||
end
|
||||
|
||||
--- Get all keys in this datastore, optional filter callback
|
||||
--[[-- Get all keys in this datastore, optional filter callback
|
||||
@tparam[opt] function callback The filter function that can be used to filter the results returned
|
||||
@treturn table All the data that is in this datastore, filtered if a filter was provided
|
||||
|
||||
@usage-- Get all the data in this datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
local data = ExampleData:get_all()
|
||||
|
||||
@usage-- Get all the data in this datastore, with a filter
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
local data = ExampleData:get_all(function(key, value)
|
||||
return type(value) == 'string'
|
||||
end)
|
||||
|
||||
]]
|
||||
function Datastore:get_all(callback)
|
||||
if not self.parent then
|
||||
return filter(self.data, callback)
|
||||
else
|
||||
local data, table_name = {}, self.table_name
|
||||
local data, value_name = {}, self.value_name
|
||||
for key, value in pairs(self.parent:get_all()) do
|
||||
data[key] = value[table_name]
|
||||
data[key] = value[value_name]
|
||||
end
|
||||
return filter(data, callback)
|
||||
end
|
||||
end
|
||||
|
||||
--- Save all the keys in the datastore, optional filter callback
|
||||
--[[-- Update all keys in this datastore using the same update function
|
||||
@tparam function callback The update function that will be applied to each key
|
||||
|
||||
@usage-- Get all the data in this datastore, with a filter
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:update_all(function(key, value)
|
||||
return value..value
|
||||
end)
|
||||
|
||||
]]
|
||||
function Datastore:update_all(callback)
|
||||
local data = self:get_all()
|
||||
for key, value in pairs(data) do
|
||||
local success, new_value = xpcall(callback, update_error, key, value)
|
||||
if success and new_value ~= nil then
|
||||
self:set(key, new_value)
|
||||
else
|
||||
self:raise_event('on_update', key, value)
|
||||
if self.auto_save then self:save(key) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----- Datastore External -----
|
||||
-- @section datastore-external
|
||||
|
||||
--[[-- Request a value from an external source, will trigger on_load when data is received
|
||||
@tparam any key The key that you want to request from an external source, must be a string unless a serializer is set
|
||||
|
||||
@usage-- Request a key from an external source, on_load is triggered when data is received
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:request('TestKey')
|
||||
|
||||
]]
|
||||
function Datastore:request(key)
|
||||
if self.parent then return self.parent:request(key) end
|
||||
key = self:serialize(key)
|
||||
self:write_action('request', key)
|
||||
end
|
||||
|
||||
--[[-- Save a value to an external source, will trigger on_save before data is saved, save_to_disk must be set to true
|
||||
@tparam any key The key that you want to save to an external source, must be a string unless a serializer is set
|
||||
|
||||
@usage-- Save a key to an external source, save_to_disk must be set to true for there to be any effect
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:save('TestKey')
|
||||
|
||||
]]
|
||||
function Datastore:save(key)
|
||||
if self.parent then self.parent:save(key) end
|
||||
if not self.save_to_disk then return end
|
||||
key = self:serialize(key)
|
||||
local value = self:raise_event('on_save', key, copy(self:raw_get(key)))
|
||||
local action = self.propagate_changes and 'propagate' or 'save'
|
||||
self:write_action(action, key, value)
|
||||
end
|
||||
|
||||
--[[-- Save a value to an external source and remove locally, will trigger on_unload then on_save, save_to_disk is not required for on_unload
|
||||
@tparam any key The key that you want to unload from the datastore, must be a string unless a serializer is set
|
||||
|
||||
@usage-- Unload a key from the datastore, get will now return nil and value will be saved externally if save_to_disk is set to true
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:unload('TestKey')
|
||||
|
||||
]]
|
||||
function Datastore:unload(key)
|
||||
if self.parent then return self.parent:unload(key) end
|
||||
key = self:serialize(key)
|
||||
self:raise_event('on_unload', key, copy(self:raw_get(key)))
|
||||
self:save(key)
|
||||
self:raw_set(key)
|
||||
end
|
||||
|
||||
--[[-- Use to send a message over the connection, works regardless of saveToDisk and propagateChanges
|
||||
@tparam any key The key that you want to send a message over, must be a string unless a serializer is set
|
||||
@tparam any message The message that you want to send to other connected servers, or external source
|
||||
|
||||
@usage-- Send a message to other servers on this key, can listen for messages with on_message
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:message('TestKey', 'Foo')
|
||||
|
||||
]]
|
||||
function Datastore:message(key, message)
|
||||
key = self:serialize(key)
|
||||
self:write_action('message', key, message)
|
||||
end
|
||||
|
||||
--[[-- Save all the keys in the datastore, optional filter callback
|
||||
@tparam[opt] function callback The filter function that can be used to filter the keys saved
|
||||
|
||||
@usage-- Save all the data in this datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
local data = ExampleData:save_all()
|
||||
|
||||
@usage-- Save all the data in this datastore, with a filter
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:save_all(function(key, value)
|
||||
return type(value) == 'string'
|
||||
end)
|
||||
|
||||
]]
|
||||
function Datastore:save_all(callback)
|
||||
local data = self:get_all(callback)
|
||||
for key in pairs(data) do self:save(key) end
|
||||
end
|
||||
|
||||
--- Unload all the keys in the datastore, optional filter callback
|
||||
--[[-- Unload all the keys in the datastore, optional filter callback
|
||||
@tparam[opt] function callback The filter function that can be used to filter the keys unloaded
|
||||
|
||||
@usage-- Unload all the data in this datastore
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:unload_all()
|
||||
|
||||
@usage-- Unload all the data in this datastore, with a filter
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:unload_all(function(key, value)
|
||||
return type(value) == 'string'
|
||||
end)
|
||||
|
||||
]]
|
||||
function Datastore:unload_all(callback)
|
||||
local data = self:get_all(callback)
|
||||
for key in pairs(data) do self:unload(key) end
|
||||
@@ -307,13 +720,23 @@ end
|
||||
----- Events -----
|
||||
-- @section events
|
||||
|
||||
--- Internal, Raise an event on this datastore
|
||||
local function event_error(err) print('An error ocurred in a datastore event handler:', err) end
|
||||
--[[-- Internal, Raise an event on this datastore
|
||||
@tparam string event_name The name of the event to raise for this datastore
|
||||
@tparam string key The key that this event is being raised for
|
||||
@tparam[opt] any value The current value that this key has, might be a deep copy of the value
|
||||
@tparam[opt] string source Where this call came from, used to do event recursion so can be parent or child
|
||||
@treturn any The value that is left after being passed through all the event handlers
|
||||
|
||||
@usage-- Internal, Getting the value that should be saved
|
||||
value = self:raise_event('on_save', key, value)
|
||||
|
||||
]]
|
||||
function Datastore:raise_event(event_name, key, value, source)
|
||||
-- Raise the event for the children of this datastore
|
||||
if source ~= 'child' then
|
||||
for table_name, child in pairs(self.children) do
|
||||
value[table_name] = child:raise_event(event_name, key, value[table_name], 'parent')
|
||||
for value_name, child in pairs(self.children) do
|
||||
value[value_name] = child:raise_event(event_name, key, value[value_name], 'parent')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -333,7 +756,14 @@ function Datastore:raise_event(event_name, key, value, source)
|
||||
return value
|
||||
end
|
||||
|
||||
--- Internal, Returns a function which will add a callback to an event
|
||||
--[[-- Internal, Returns a function which will add a callback to an event
|
||||
@tparam string event_name The name of the event that this should create a handler adder for
|
||||
@treturn function The function that can be used to add handlers to this event
|
||||
|
||||
@usage-- Internal, Get the function to add handlers to on_load
|
||||
Datastore.on_load = event_factory('on_load')
|
||||
|
||||
]]
|
||||
local function event_factory(event_name)
|
||||
return function(self, callback)
|
||||
assert(type(callback) == 'function', 'Handler must be a function')
|
||||
@@ -346,19 +776,54 @@ local function event_factory(event_name)
|
||||
end
|
||||
end
|
||||
|
||||
--- Register a callback that triggers when data is loaded from an external source, returned value is saved locally
|
||||
--[[-- Register a callback that triggers when data is loaded from an external source, returned value is saved locally
|
||||
@tparam function callback The handler that will be registered to the on_load event
|
||||
@usage-- Adding a handler to on_load, returned value will be saved locally, can be used to deserialize the value beyond a normal json
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:on_load(function(key, value)
|
||||
game.print('Test data loaded for: '..key)
|
||||
end)
|
||||
]]
|
||||
Datastore.on_load = event_factory('on_load')
|
||||
|
||||
--- Register a callback that triggers before data is saved, returned value is saved externally
|
||||
--[[-- Register a callback that triggers before data is saved, returned value is saved externally
|
||||
@tparam function callback The handler that will be registered to the on_load event
|
||||
@usage-- Adding a handler to on_save, returned value will be saved externally, can be used to serialize the value beyond a normal json
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:on_save(function(key, value)
|
||||
game.print('Test data saved for: '..key)
|
||||
end)
|
||||
]]
|
||||
Datastore.on_save = event_factory('on_save')
|
||||
|
||||
--- Register a callback that triggers before data is unloaded, returned value is ignored
|
||||
--[[-- Register a callback that triggers before data is unloaded, returned value is ignored
|
||||
@tparam function callback The handler that will be registered to the on_load event
|
||||
@usage-- Adding a handler to on_unload, returned value is ignored, can be used to clean up guis or local values related to this data
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:on_load(function(key, value)
|
||||
game.print('Test data unloaded for: '..key)
|
||||
end)
|
||||
]]
|
||||
Datastore.on_unload = event_factory('on_unload')
|
||||
|
||||
--- Register a callback that triggers when a message is received, returned value is ignored
|
||||
--[[-- Register a callback that triggers when a message is received, returned value is ignored
|
||||
@tparam function callback The handler that will be registered to the on_load event
|
||||
@usage-- Adding a handler to on_message, returned value is ignored, can be used to receive messages from other connected servers without saving data
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:on_message(function(key, value)
|
||||
game.print('Test data message for: '..key)
|
||||
end)
|
||||
]]
|
||||
Datastore.on_message = event_factory('on_message')
|
||||
|
||||
--- Register a callback that triggers any time a value is changed, returned value is ignored
|
||||
--[[-- Register a callback that triggers any time a value is changed, returned value is ignored
|
||||
@tparam function callback The handler that will be registered to the on_load event
|
||||
@usage-- Adding a handler to on_update, returned value is ignored, can be used to update guis or send messages when data is changed
|
||||
local ExampleData = Datastore.connect('ExampleData')
|
||||
ExampleData:on_update(function(key, value)
|
||||
game.print('Test data updated for: '..key)
|
||||
end)
|
||||
]]
|
||||
Datastore.on_update = event_factory('on_update')
|
||||
|
||||
----- Module Return -----
|
||||
|
||||
Reference in New Issue
Block a user