Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- This system does not cover edge cases:
- - Player leaves and rejoins quickly
- - Player joins, data doesn't fully load, game crashes (bindToClose fires)
- API:
- - setup()
- - savePlayerData(player)
- - *STUDIO ONLY* wipePlayerData(player)
- - bindToPlayerDataLoaded(func)
- - changeTokens(player)
- - addCharacter(player)
- - addDoor(player)
- - getTokens(player)
- - getDoors(player)
- - getCharacters(player)
- - loadedDataOk(player)
- - hasDataLoaded(player)
- - hasDoor(player)
- - hasCharacter(player)
- --]]
- local PlayerStatManager = {}
- local Players = game:GetService("Players")
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local DataStoreService = game:GetService("DataStoreService")
- local PlayerData = DataStoreService:GetDataStore("PlayerData")
- local cRequire = require(ReplicatedStorage:WaitForChild("CustomRequire"))
- local RemotesHandler = cRequire("RemotesHandler")
- local CombatCharacterService = cRequire("CombatCharacterService")
- local DoorService = cRequire("DoorService")
- local isPlayer = cRequire("IsPlayer")
- local sessionData = {}
- local loadedDataOk = {}
- local onPlayerDataLoaded
- local DATA_VERSION = 1
- local STARTER_TOKENS = 0
- local MINIMUM_TOKENS = 0
- local getStarterData = function ()
- return
- {
- ["Tokens"] = STARTER_TOKENS,
- ["Characters"] = {["001"]=true, ["002"]=true, ["003"]=true, ["004"]=true, ["005"]=true},
- ["Doors"] = {},
- ["DataVersion"] = DATA_VERSION
- }
- end
- -- NOTE: for players like tommy/andy who have old data,
- -- this won't work - find a fix to this -> edit data stores
- function PlayerStatManager.setup()
- local function setupPlayerData(player)
- sessionData[player.UserId] = {}
- -- PlayerData
- local success, data = pcall(function ()
- return(PlayerData:GetAsync(tostring(player.UserId)))
- end)
- if success then
- if data then
- -- Check and replace outdated data versions
- if data.DataVersion and data.DataVersion == DATA_VERSION then
- sessionData[player.UserId] = data
- else
- print'wrong data version loaded, setting new'
- sessionData[player.UserId] = getStarterData()
- end
- else
- -- First time playing - give them default data
- sessionData[player.UserId] = getStarterData()
- end
- loadedDataOk[player.UserId] = true
- if onPlayerDataLoaded then
- onPlayerDataLoaded(player, sessionData[player.UserId])
- end
- else
- -- Player's data failed to load
- warn(player.Name.."s data failed to load! Data will not save for this player.")
- sessionData[player.UserId] = getStarterData()
- end
- if player.Parent then
- -- Code below runs when player leaves
- player:GetPropertyChangedSignal("Parent"):Connect(function ()
- if player.Parent == nil then
- PlayerStatManager.savePlayerData(player)
- sessionData[player.UserId] = nil
- if loadedDataOk[player.UserId] then
- loadedDataOk[player.UserId] = nil
- end
- end
- end)
- else
- -- Covers edge case: Player leaves before leave connection added
- sessionData[player.UserId] = nil
- end
- end
- local function saveAllPlayerData()
- for _, player in pairs(Players:GetPlayers()) do
- PlayerStatManager.savePlayerData(player)
- end
- end
- for _, player in pairs(Players:GetPlayers()) do
- setupPlayerData(player)
- end
- Players.PlayerAdded:Connect(setupPlayerData)
- -- Todo: check if this bind to close is safe
- game:BindToClose(saveAllPlayerData)
- -- Requests
- local getDataRF = RemotesHandler.newRemoteFunction("OnPlayerDataRequest")
- getDataRF.OnServerInvoke = function (player)
- return sessionData[player.UserId]
- end
- end
- function PlayerStatManager.savePlayerData(player)
- assert(isPlayer(player))
- if not loadedDataOk[player.UserId] then
- warn(player.Name.."'s data didn't save. Player's data didn't load")
- return false
- end
- PlayerData:SetAsync(tostring(player.UserId), sessionData[player.UserId])
- return true
- end
- -- Studio only function for testing
- function PlayerStatManager.wipePlayerData(player)
- assert(isPlayer(player))
- assert(game:GetService("RunService"):IsStudio())
- loadedDataOk[player.UserId] = nil
- PlayerData:RemoveAsync(player.UserId)
- print(player.Name.."'s data cleared")
- end
- function PlayerStatManager.bindToPlayerDataLoaded(func)
- onPlayerDataLoaded = func
- end
- ---------------------- MUTATORS ----------------------
- function PlayerStatManager.changeTokens(player, changeValue)
- assert(isPlayer(player))
- assert(typeof(changeValue) == "number" and changeValue%1 == 0)
- sessionData[player.UserId].Tokens = sessionData[player.UserId].Tokens + changeValue
- -- Don't allow negative token balance
- if sessionData[player.UserId].Tokens < MINIMUM_TOKENS then
- sessionData[player.UserId].Tokens = MINIMUM_TOKENS
- end
- end
- function PlayerStatManager.addCharacter(player, idOrName)
- assert(isPlayer(player))
- assert(CombatCharacterService.getCharacterFromIdOrName(idOrName))
- local requestedCharacter = CombatCharacterService.getCharacterFromIdOrName(idOrName)
- if requestedCharacter.Wave == "DLC" then
- error("Attempt to save "..requestedCharacter.Name.." to "..player.Name.."'s owned characters")
- end
- sessionData[player.UserId].Characters[requestedCharacter.Id] = true
- end
- function PlayerStatManager.addDoor(player, doorId)
- assert(isPlayer(player))
- assert(DoorService.getDoor(doorId))
- sessionData[player.UserId].Doors[doorId] = true
- end
- ---------------------- ACCESSORS ----------------------
- function PlayerStatManager.getTokens(player)
- assert(isPlayer(player))
- return sessionData[player.UserId].Tokens
- end
- function PlayerStatManager.getDoors(player)
- assert(isPlayer(player))
- -- Create new table so that session data one cannot be modified
- local proxyTable = {}
- for k, v in pairs(sessionData[player.UserId].Doors) do
- proxyTable[k] = v
- end
- return proxyTable
- end
- -- Note: Does not include DLC characters
- function PlayerStatManager.getCharacters(player)
- assert(isPlayer(player))
- -- Create new table so that session data one cannot be modified
- local characterTable = {}
- for k, v in pairs(sessionData[player.UserId].Characters) do
- characterTable[k] = v
- end
- return characterTable
- end
- function PlayerStatManager.loadedDataOk(player)
- assert(isPlayer(player))
- return loadedDataOk[player.UserId]
- end
- function PlayerStatManager.hasDataLoaded(player)
- assert(isPlayer(player))
- return sessionData[player.UserId] and true or false
- end
- function PlayerStatManager.hasDoor(player, doorId)
- assert(isPlayer(player))
- assert(typeof(doorId) == "string")
- assert(DoorService.getDoor(doorId))
- return sessionData[player.UserId]["Doors"][doorId] or false
- end
- function PlayerStatManager.hasCharacter(player, characterId)
- assert(isPlayer(player))
- assert(typeof(characterId) == "string")
- local requestedCharacter = CombatCharacterService.getCharacterFromIdOrName(characterId)
- assert(requestedCharacter)
- if requestedCharacter.Wave == "DLC" then
- print'checking if dlc owned...'
- end
- return sessionData[player.UserId]["Characters"][characterId] or false
- end
- return PlayerStatManager
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement