For the past couple of days I’ve been trying how I could implement metamethods with DataStoreService and recently I’ve found how I could do it. Any improvements I can apply onto my module?
local Data = {}
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local DATASTORE = DataStoreService:GetDataStore("data_1")
local dataProfiles = {}
local Profiles = {
--[[
["51739716"] = {
Money = 100,
Gems = 500,
Deaths = 50
}
]]
}
local Signal = require(script.Signal)
local activeTemplate = nil -- template that is actively being used and has already been set by :SetTemplate
--[[
local activeTemplate = {
Money = 100,
Gems = 500,
Deaths = 50
}
]]
local templateLoaded = false
-- Private Functions
local function Save(player: Player?)
if typeof(player) ~= "Instance" then
error(string.format("Player expected, got %s", typeof(player)))
end
local success, result = pcall(function()
DATASTORE:UpdateAsync("PLAYER_" .. player.UserId, function(oldVal)
return Profiles[player.UserId]
end)
end)
if success then
print(string.format("Saving %s's data is a success", player.Name))
else
print(string.format("Unable to save %s's data, result: %s", player.Name, result))
end
end
local function Load(player: Player?)
if typeof(player) ~= "Instance" then
error(string.format("Player expected, got %s", typeof(player)))
end
local data
local success, result = pcall(function()
data = DATASTORE:GetAsync("PLAYER_" .. player.UserId) or activeTemplate
end)
if success then
print("Data successfully loaded for player via :GetAsync()")
return data
else
player:Kick("Unable to load your data, please rejoin the game.")
print(result)
end
end
local function onPlayerAdded(player: Player)
if typeof(player) ~= "Instance" then
error(string.format("Player expected, got %s", typeof(player)))
end
if activeTemplate == nil then
error("activeTemplate has not been loaded in")
end
local data = Load(player)
local dataProfile = {}
local mt = {__events = {}}
-- loop through given template, and add the signals
for property, value in pairs(activeTemplate) do
-- only accept properties that are numbers to have signals
if typeof(value) == "number" then
mt.__events[property] = Signal.new()
end
end
mt.__newindex = function(t, k, v)
-- if the indexed value is inside the template
if activeTemplate[k] then
-- if the value we're changing with is the same as the property
if typeof(v) == typeof(activeTemplate[k]) then
Profiles[player.UserId][k] = v
-- if a signal exists for the property
if mt.__events[k] then
return mt.__events[k]:Fire(v)
end
else
error(string.format("Unable to assign property %s. %s expected, got %s", k, typeof(activeTemplate[k]), typeof(v)))
end
else
error(string.format("%s is not a valid member of activeTemplate", k))
end
end
mt.__index = function(t, k)
-- if the indexed value is in the template
if activeTemplate[k] then
-- return the player's indexed data
return Profiles[player.UserId][k]
else
error(string.format("%s is not a valid member of activeTemplate", k))
end
end
mt.__tostring = function(t)
return "DataClass"
end
function dataProfile:GetPropertyChangedSignal(property)
local signal = mt.__events[property]
if signal then return signal end
end
setmetatable(dataProfile, mt)
dataProfiles[player.UserId] = dataProfile
Profiles[player.UserId] = data
end
local function onPlayerRemoving(player: Player)
if typeof(player) ~= "Instance" then
error(string.format("Player expected, got %s", typeof(player)))
end
Save(player)
Profiles[player.UserId] = nil
-- disconnect all of the signals
for _, signal in pairs(getmetatable(dataProfiles[player.UserId]).__events) do
signal:DisconnectAll()
end
dataProfiles[player.UserId] = nil
end
-- Main Functions
function Data:SetTemplate(template)
if typeof(template) ~= "table" then
string.format(error("Unable to assign 'template' %s expected, got %s", "table", typeof(template)))
end
activeTemplate = template
templateLoaded = true
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)
-- in case if there are players who joined before this script has been executed
for _, player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
end
function Data:GetData(player: Player)
if typeof(player) ~= "Instance" and not player:IsA("Player") then
error(string.format("Player expected, got %s", typeof(player)))
end
if not templateLoaded then
error("Unable to get data if you haven't set a template yet using :SetTemplate()")
end
local profile = dataProfiles[player.UserId]
if profile == nil then
repeat
profile = dataProfiles[player.UserId]
task.wait(.1)
until profile ~= nil
else
return profile
end
return profile
end
return Data