@quenty/datastore 13.20.0 → 13.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +3 -3
- package/package.json +15 -15
- package/src/Server/DataStore.lua +32 -24
- package/src/Server/GameDataStoreService.lua +2 -1
- package/src/Server/Modules/DataStoreSnapshotUtils.lua +1 -1
- package/src/Server/Modules/DataStoreStage.lua +86 -69
- package/src/Server/Modules/DataStoreWriter.lua +30 -12
- package/src/Server/PlayerDataStoreManager.lua +10 -10
- package/src/Server/PlayerDataStoreService.lua +4 -3
- package/src/Server/PrivateServerDataStoreService.lua +2 -1
- package/src/Server/Utility/DataStorePromises.lua +34 -14
- package/test/scripts/Server/ServerMain.server.lua +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [13.20.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/datastore@13.20.0...@quenty/datastore@13.20.1) (2025-04-05)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [13.20.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/datastore@13.19.3...@quenty/datastore@13.20.0) (2025-04-02)
|
|
7
18
|
|
|
8
19
|
**Note:** Version bump only for package @quenty/datastore
|
package/README.md
CHANGED
|
@@ -26,9 +26,9 @@ This datastore prevents data loss by being explicit about what we're writing to,
|
|
|
26
26
|
* Native Rx and Promise implementation
|
|
27
27
|
|
|
28
28
|
## How syncing works
|
|
29
|
-
Sometimes datastores (like a global game data store) need to be synced live instead of upon server or player start. This is if we expect multiple servers to write to the same datastore at once we can use thie sync method to
|
|
29
|
+
Sometimes datastores (like a global game data store) need to be synced live instead of upon server or player start. This is if we expect multiple servers to write to the same datastore at once we can use thie sync method to
|
|
30
30
|
|
|
31
|
-
Syncing is like saving. However, instead of treating the current datastore as a session lock, we load in additional data from our "source-of-truth". From here, we merge that data into the datastore, which means both clearing any matching write tokens that our sync says is done.
|
|
31
|
+
Syncing is like saving. However, instead of treating the current datastore as a session lock, we load in additional data from our "source-of-truth". From here, we merge that data into the datastore, which means both clearing any matching write tokens that our sync says is done.
|
|
32
32
|
|
|
33
33
|
This is best for a "shared" memory that can be temporarily not correct. Deleting with a sync is less effective.
|
|
34
34
|
|
|
@@ -394,7 +394,7 @@ end
|
|
|
394
394
|
-- Do actual load
|
|
395
395
|
|
|
396
396
|
Players.PlayerAdded:Connect(handlePlayerAdded)
|
|
397
|
-
for _, player in
|
|
397
|
+
for _, player in Players:GetPlayers() do
|
|
398
398
|
handlePlayerAdded(player)
|
|
399
399
|
end
|
|
400
400
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/datastore",
|
|
3
|
-
"version": "13.20.
|
|
3
|
+
"version": "13.20.1",
|
|
4
4
|
"description": "Quenty's Datastore implementation for Roblox",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -26,22 +26,22 @@
|
|
|
26
26
|
"Quenty"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@quenty/baseobject": "^10.8.
|
|
30
|
-
"@quenty/bindtocloseservice": "^8.17.
|
|
31
|
-
"@quenty/loader": "^10.8.
|
|
32
|
-
"@quenty/maid": "^3.4.
|
|
33
|
-
"@quenty/math": "^2.7.
|
|
34
|
-
"@quenty/pagesutils": "^5.11.
|
|
35
|
-
"@quenty/promise": "^10.10.
|
|
36
|
-
"@quenty/rx": "^13.17.
|
|
37
|
-
"@quenty/servicebag": "^11.11.
|
|
38
|
-
"@quenty/signal": "^7.10.
|
|
39
|
-
"@quenty/symbol": "^3.4.
|
|
40
|
-
"@quenty/table": "^3.7.
|
|
41
|
-
"@quenty/valueobject": "^13.17.
|
|
29
|
+
"@quenty/baseobject": "^10.8.1",
|
|
30
|
+
"@quenty/bindtocloseservice": "^8.17.1",
|
|
31
|
+
"@quenty/loader": "^10.8.1",
|
|
32
|
+
"@quenty/maid": "^3.4.1",
|
|
33
|
+
"@quenty/math": "^2.7.2",
|
|
34
|
+
"@quenty/pagesutils": "^5.11.2",
|
|
35
|
+
"@quenty/promise": "^10.10.2",
|
|
36
|
+
"@quenty/rx": "^13.17.1",
|
|
37
|
+
"@quenty/servicebag": "^11.11.2",
|
|
38
|
+
"@quenty/signal": "^7.10.1",
|
|
39
|
+
"@quenty/symbol": "^3.4.1",
|
|
40
|
+
"@quenty/table": "^3.7.2",
|
|
41
|
+
"@quenty/valueobject": "^13.17.1"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "78c3ac0ab08dd18085b6e6e6e4f745e76ed99f68"
|
|
47
47
|
}
|
package/src/Server/DataStore.lua
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
local topMaid = Maid.new()
|
|
34
34
|
|
|
35
|
-
local function handlePlayer(player)
|
|
35
|
+
local function handlePlayer(player: Player)
|
|
36
36
|
local maid = Maid.new()
|
|
37
37
|
|
|
38
38
|
local playerMoneyValue = Instance.new("IntValue")
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
Players.PlayerRemoving:Connect(function(player)
|
|
55
55
|
topMaid[player] = nil
|
|
56
56
|
end)
|
|
57
|
-
for _, player in
|
|
57
|
+
for _, player in Players:GetPlayers() do
|
|
58
58
|
task.spawn(handlePlayer, player)
|
|
59
59
|
end
|
|
60
60
|
```
|
|
@@ -85,6 +85,8 @@ local DataStore = setmetatable({}, DataStoreStage)
|
|
|
85
85
|
DataStore.ClassName = "DataStore"
|
|
86
86
|
DataStore.__index = DataStore
|
|
87
87
|
|
|
88
|
+
export type DataStore = typeof(setmetatable({}, { __index = DataStore }))
|
|
89
|
+
|
|
88
90
|
--[=[
|
|
89
91
|
Constructs a new DataStore. See [DataStoreStage] for more API.
|
|
90
92
|
|
|
@@ -96,7 +98,7 @@ DataStore.__index = DataStore
|
|
|
96
98
|
@param key string
|
|
97
99
|
@return DataStore
|
|
98
100
|
]=]
|
|
99
|
-
function DataStore.new(robloxDataStore, key)
|
|
101
|
+
function DataStore.new(robloxDataStore, key: string)
|
|
100
102
|
local self = setmetatable(DataStoreStage.new(key), DataStore)
|
|
101
103
|
|
|
102
104
|
self._key = key or error("No key")
|
|
@@ -114,7 +116,7 @@ function DataStore.new(robloxDataStore, key)
|
|
|
114
116
|
error("[DataStore] - Key cannot be an empty string")
|
|
115
117
|
end
|
|
116
118
|
|
|
117
|
-
--[=[
|
|
119
|
+
--[=[
|
|
118
120
|
Prop that fires when saving. Promise will resolve once saving is complete.
|
|
119
121
|
@prop Saving Signal<Promise>
|
|
120
122
|
@within DataStore
|
|
@@ -131,7 +133,7 @@ end
|
|
|
131
133
|
|
|
132
134
|
@param debugWriting boolean
|
|
133
135
|
]=]
|
|
134
|
-
function DataStore:SetDoDebugWriting(debugWriting)
|
|
136
|
+
function DataStore:SetDoDebugWriting(debugWriting: boolean)
|
|
135
137
|
assert(type(debugWriting) == "boolean", "Bad debugWriting")
|
|
136
138
|
|
|
137
139
|
self._debugWriting = debugWriting
|
|
@@ -141,7 +143,7 @@ end
|
|
|
141
143
|
Returns the full path for the datastore
|
|
142
144
|
@return string
|
|
143
145
|
]=]
|
|
144
|
-
function DataStore:GetFullPath()
|
|
146
|
+
function DataStore:GetFullPath(): string
|
|
145
147
|
return string.format("RobloxDataStore@%s", self._key)
|
|
146
148
|
end
|
|
147
149
|
|
|
@@ -149,9 +151,9 @@ end
|
|
|
149
151
|
How frequent the data store will autosave (or sync) to the cloud. If set to nil then the datastore
|
|
150
152
|
will not do any syncing.
|
|
151
153
|
|
|
152
|
-
@param autoSaveTimeSeconds number
|
|
154
|
+
@param autoSaveTimeSeconds number?
|
|
153
155
|
]=]
|
|
154
|
-
function DataStore:SetAutoSaveTimeSeconds(autoSaveTimeSeconds)
|
|
156
|
+
function DataStore:SetAutoSaveTimeSeconds(autoSaveTimeSeconds: number?)
|
|
155
157
|
assert(type(autoSaveTimeSeconds) == "number" or autoSaveTimeSeconds == nil, "Bad autoSaveTimeSeconds")
|
|
156
158
|
|
|
157
159
|
self._autoSaveTimeSeconds.Value = autoSaveTimeSeconds
|
|
@@ -162,7 +164,7 @@ end
|
|
|
162
164
|
|
|
163
165
|
@param syncEnabled boolean
|
|
164
166
|
]=]
|
|
165
|
-
function DataStore:SetSyncOnSave(syncEnabled)
|
|
167
|
+
function DataStore:SetSyncOnSave(syncEnabled: boolean)
|
|
166
168
|
assert(type(syncEnabled) == "boolean", "Bad syncEnabled")
|
|
167
169
|
|
|
168
170
|
self._syncOnSave.Value = syncEnabled
|
|
@@ -172,7 +174,7 @@ end
|
|
|
172
174
|
Returns whether the datastore failed.
|
|
173
175
|
@return boolean
|
|
174
176
|
]=]
|
|
175
|
-
function DataStore:DidLoadFail()
|
|
177
|
+
function DataStore:DidLoadFail(): boolean
|
|
176
178
|
if not self._firstLoadPromise then
|
|
177
179
|
return false
|
|
178
180
|
end
|
|
@@ -189,7 +191,7 @@ end
|
|
|
189
191
|
|
|
190
192
|
@return Promise<boolean>
|
|
191
193
|
]=]
|
|
192
|
-
function DataStore:PromiseLoadSuccessful()
|
|
194
|
+
function DataStore:PromiseLoadSuccessful(): Promise.Promise<boolean>
|
|
193
195
|
return self._maid:GivePromise(self:PromiseViewUpToDate()):Then(function()
|
|
194
196
|
return true
|
|
195
197
|
end, function()
|
|
@@ -201,7 +203,7 @@ end
|
|
|
201
203
|
Saves all stored data.
|
|
202
204
|
@return Promise
|
|
203
205
|
]=]
|
|
204
|
-
function DataStore:Save()
|
|
206
|
+
function DataStore:Save(): Promise.Promise<()>
|
|
205
207
|
return self:_syncData(false)
|
|
206
208
|
end
|
|
207
209
|
|
|
@@ -211,16 +213,16 @@ end
|
|
|
211
213
|
|
|
212
214
|
@return Promise
|
|
213
215
|
]=]
|
|
214
|
-
function DataStore:Sync()
|
|
216
|
+
function DataStore:Sync(): Promise.Promise<()>
|
|
215
217
|
return self:_syncData(true)
|
|
216
218
|
end
|
|
217
219
|
|
|
218
220
|
--[=[
|
|
219
221
|
Sets the user id list associated with this datastore. Can be useful for GDPR compliance.
|
|
220
222
|
|
|
221
|
-
@param userIdList { number }
|
|
223
|
+
@param userIdList { number }?
|
|
222
224
|
]=]
|
|
223
|
-
function DataStore:SetUserIdList(userIdList)
|
|
225
|
+
function DataStore:SetUserIdList(userIdList: { number }?)
|
|
224
226
|
assert(type(userIdList) == "table" or userIdList == nil, "Bad userIdList")
|
|
225
227
|
assert(not Symbol.isSymbol(userIdList), "Should not be symbol")
|
|
226
228
|
|
|
@@ -230,9 +232,9 @@ end
|
|
|
230
232
|
--[=[
|
|
231
233
|
Returns a list of user ids or nil
|
|
232
234
|
|
|
233
|
-
@return { number }
|
|
235
|
+
@return { number }?
|
|
234
236
|
]=]
|
|
235
|
-
function DataStore:GetUserIdList()
|
|
237
|
+
function DataStore:GetUserIdList(): { number }?
|
|
236
238
|
return self._userIdList
|
|
237
239
|
end
|
|
238
240
|
|
|
@@ -241,7 +243,7 @@ end
|
|
|
241
243
|
|
|
242
244
|
@return Promise
|
|
243
245
|
]=]
|
|
244
|
-
function DataStore:PromiseViewUpToDate()
|
|
246
|
+
function DataStore:PromiseViewUpToDate(): Promise.Promise<()>
|
|
245
247
|
if self._firstLoadPromise then
|
|
246
248
|
return self._firstLoadPromise
|
|
247
249
|
end
|
|
@@ -259,10 +261,10 @@ function DataStore:_setupAutoSaving()
|
|
|
259
261
|
local startTime = os.clock()
|
|
260
262
|
|
|
261
263
|
self._maid:GiveTask(Rx.combineLatest({
|
|
262
|
-
autoSaveTimeSeconds = self._autoSaveTimeSeconds:Observe()
|
|
263
|
-
jitterProportion = self._jitterProportion:Observe()
|
|
264
|
-
syncOnSave = self._syncOnSave:Observe()
|
|
265
|
-
loadedOk = self._loadedOk:Observe()
|
|
264
|
+
autoSaveTimeSeconds = self._autoSaveTimeSeconds:Observe(),
|
|
265
|
+
jitterProportion = self._jitterProportion:Observe(),
|
|
266
|
+
syncOnSave = self._syncOnSave:Observe(),
|
|
267
|
+
loadedOk = self._loadedOk:Observe(),
|
|
266
268
|
}):Subscribe(function(state)
|
|
267
269
|
if state.autoSaveTimeSeconds and state.loadedOk then
|
|
268
270
|
local maid = Maid.new()
|
|
@@ -275,7 +277,11 @@ function DataStore:_setupAutoSaving()
|
|
|
275
277
|
while true do
|
|
276
278
|
local jitterBase = math.random()
|
|
277
279
|
local timeElapsed = os.clock() - startTime
|
|
278
|
-
local totalWaitTime = Math.jitter(
|
|
280
|
+
local totalWaitTime = Math.jitter(
|
|
281
|
+
state.autoSaveTimeSeconds,
|
|
282
|
+
state.jitterProportion * state.autoSaveTimeSeconds,
|
|
283
|
+
jitterBase
|
|
284
|
+
)
|
|
279
285
|
local timeRemaining = totalWaitTime - timeElapsed
|
|
280
286
|
|
|
281
287
|
if timeRemaining > 0 then
|
|
@@ -301,7 +307,7 @@ function DataStore:_setupAutoSaving()
|
|
|
301
307
|
end))
|
|
302
308
|
end
|
|
303
309
|
|
|
304
|
-
function DataStore:_syncData(doMergeNewData)
|
|
310
|
+
function DataStore:_syncData(doMergeNewData: boolean)
|
|
305
311
|
if self:DidLoadFail() then
|
|
306
312
|
warn("[DataStore] - Not syncing, failed to load")
|
|
307
313
|
return Promise.rejected("Load not successful, not syncing")
|
|
@@ -354,6 +360,8 @@ function DataStore:_doDataSync(writer, doMergeNewData)
|
|
|
354
360
|
-- Do syncing after
|
|
355
361
|
return self:_promiseGetAsyncNoCache()
|
|
356
362
|
end
|
|
363
|
+
|
|
364
|
+
return nil
|
|
357
365
|
end))
|
|
358
366
|
else
|
|
359
367
|
if self._debugWriting then
|
|
@@ -12,11 +12,12 @@ local DataStore = require("DataStore")
|
|
|
12
12
|
local DataStorePromises = require("DataStorePromises")
|
|
13
13
|
local Maid = require("Maid")
|
|
14
14
|
local Promise = require("Promise")
|
|
15
|
+
local _ServiceBag = require("ServiceBag")
|
|
15
16
|
|
|
16
17
|
local GameDataStoreService = {}
|
|
17
18
|
GameDataStoreService.ServiceName = "GameDataStoreService"
|
|
18
19
|
|
|
19
|
-
function GameDataStoreService:Init(serviceBag)
|
|
20
|
+
function GameDataStoreService:Init(serviceBag: _ServiceBag.ServiceBag)
|
|
20
21
|
assert(not self._serviceBag, "Already initialized")
|
|
21
22
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
22
23
|
|
|
@@ -8,7 +8,7 @@ local Symbol = require("Symbol")
|
|
|
8
8
|
|
|
9
9
|
local DataStoreSnapshotUtils = {}
|
|
10
10
|
|
|
11
|
-
function DataStoreSnapshotUtils.isEmptySnapshot(snapshot)
|
|
11
|
+
function DataStoreSnapshotUtils.isEmptySnapshot(snapshot: any): boolean
|
|
12
12
|
return not Symbol.isSymbol(snapshot) and type(snapshot) == "table" and next(snapshot) == nil
|
|
13
13
|
end
|
|
14
14
|
|
|
@@ -20,7 +20,7 @@ local BaseObject = require("BaseObject")
|
|
|
20
20
|
local DataStoreDeleteToken = require("DataStoreDeleteToken")
|
|
21
21
|
local DataStoreSnapshotUtils = require("DataStoreSnapshotUtils")
|
|
22
22
|
local DataStoreWriter = require("DataStoreWriter")
|
|
23
|
-
local
|
|
23
|
+
local Signal = require("Signal")
|
|
24
24
|
local Maid = require("Maid")
|
|
25
25
|
local Observable = require("Observable")
|
|
26
26
|
local ObservableSubscriptionTable = require("ObservableSubscriptionTable")
|
|
@@ -36,6 +36,14 @@ local DataStoreStage = setmetatable({}, BaseObject)
|
|
|
36
36
|
DataStoreStage.ClassName = "DataStoreStage"
|
|
37
37
|
DataStoreStage.__index = DataStoreStage
|
|
38
38
|
|
|
39
|
+
export type DataStoreStage = typeof(setmetatable(
|
|
40
|
+
{} :: {
|
|
41
|
+
Changed: Signal.Signal<any>,
|
|
42
|
+
DataStored: Signal.Signal<any>,
|
|
43
|
+
},
|
|
44
|
+
DataStoreStage
|
|
45
|
+
))
|
|
46
|
+
|
|
39
47
|
--[=[
|
|
40
48
|
Constructs a new DataStoreStage to load from. Prefer to use DataStore because this doesn't
|
|
41
49
|
have any way to retrieve this.
|
|
@@ -51,15 +59,15 @@ DataStoreStage.__index = DataStoreStage
|
|
|
51
59
|
@param loadParent DataStoreStage?
|
|
52
60
|
@return DataStoreStage
|
|
53
61
|
]=]
|
|
54
|
-
function DataStoreStage.new(loadName, loadParent)
|
|
62
|
+
function DataStoreStage.new(loadName: string, loadParent: DataStoreStage?)
|
|
55
63
|
local self = setmetatable(BaseObject.new(), DataStoreStage)
|
|
56
64
|
|
|
57
65
|
-- LoadParent is optional, used for loading
|
|
58
66
|
self._loadName = loadName
|
|
59
67
|
self._loadParent = loadParent
|
|
60
68
|
|
|
61
|
-
self.Changed = self._maid:Add(
|
|
62
|
-
self.DataStored = self._maid:Add(
|
|
69
|
+
self.Changed = self._maid:Add(Signal.new()) -- :Fire(viewSnapshot)
|
|
70
|
+
self.DataStored = self._maid:Add(Signal.new())
|
|
63
71
|
|
|
64
72
|
-- Stores the actual data loaded and synced (but not pending written data)
|
|
65
73
|
self._saveDataSnapshot = nil
|
|
@@ -83,10 +91,10 @@ end
|
|
|
83
91
|
dataStore:Store("money", 25)
|
|
84
92
|
```
|
|
85
93
|
|
|
86
|
-
@param key string
|
|
94
|
+
@param key string
|
|
87
95
|
@param value any
|
|
88
96
|
]=]
|
|
89
|
-
function DataStoreStage:Store(key, value)
|
|
97
|
+
function DataStoreStage:Store(key: string, value: any)
|
|
90
98
|
assert(type(key) == "string", "Bad key")
|
|
91
99
|
|
|
92
100
|
if value == nil then
|
|
@@ -112,7 +120,7 @@ end
|
|
|
112
120
|
@param defaultValue T?
|
|
113
121
|
@return Promise<T>
|
|
114
122
|
]=]
|
|
115
|
-
function DataStoreStage:Load(key, defaultValue)
|
|
123
|
+
function DataStoreStage:Load<T>(key: string | number, defaultValue: T?): Promise.Promise<T>
|
|
116
124
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
117
125
|
|
|
118
126
|
return self:PromiseViewUpToDate():Then(function()
|
|
@@ -165,7 +173,7 @@ end
|
|
|
165
173
|
@param key string | number
|
|
166
174
|
@return DataStoreStage
|
|
167
175
|
]=]
|
|
168
|
-
function DataStoreStage:GetSubStore(key)
|
|
176
|
+
function DataStoreStage:GetSubStore(key: string | number)
|
|
169
177
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
170
178
|
|
|
171
179
|
if self._stores[key] then
|
|
@@ -220,7 +228,7 @@ end
|
|
|
220
228
|
|
|
221
229
|
@param key string | number
|
|
222
230
|
]=]
|
|
223
|
-
function DataStoreStage:Delete(key)
|
|
231
|
+
function DataStoreStage:Delete(key: string)
|
|
224
232
|
assert(type(key) == "string", "Bad key")
|
|
225
233
|
|
|
226
234
|
self:_storeAtKey(key, DataStoreDeleteToken)
|
|
@@ -238,7 +246,7 @@ end
|
|
|
238
246
|
|
|
239
247
|
If no key is passed than it will observe the whole view snapshot
|
|
240
248
|
|
|
241
|
-
@param key string | number
|
|
249
|
+
@param key string | number?
|
|
242
250
|
@param defaultValue T?
|
|
243
251
|
@return Observable<T>
|
|
244
252
|
]=]
|
|
@@ -248,25 +256,24 @@ function DataStoreStage:Observe(key, defaultValue)
|
|
|
248
256
|
if key == nil then
|
|
249
257
|
return Observable.new(function(sub)
|
|
250
258
|
local maid = Maid.new()
|
|
251
|
-
maid:GivePromise(self:LoadAll())
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if viewSnapshot == nil then
|
|
256
|
-
sub:Fire(defaultValue)
|
|
257
|
-
else
|
|
258
|
-
sub:Fire(viewSnapshot)
|
|
259
|
-
end
|
|
260
|
-
end))
|
|
261
|
-
|
|
262
|
-
if self._viewSnapshot == nil then
|
|
259
|
+
maid:GivePromise(self:LoadAll()):Then(function()
|
|
260
|
+
-- Only connect once loaded
|
|
261
|
+
maid:GiveTask(self.Changed:Connect(function(viewSnapshot)
|
|
262
|
+
if viewSnapshot == nil then
|
|
263
263
|
sub:Fire(defaultValue)
|
|
264
264
|
else
|
|
265
|
-
sub:Fire(
|
|
265
|
+
sub:Fire(viewSnapshot)
|
|
266
266
|
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
end))
|
|
268
|
+
|
|
269
|
+
if self._viewSnapshot == nil then
|
|
270
|
+
sub:Fire(defaultValue)
|
|
271
|
+
else
|
|
272
|
+
sub:Fire(self._viewSnapshot)
|
|
273
|
+
end
|
|
274
|
+
end, function(...)
|
|
275
|
+
sub:Fail(...)
|
|
276
|
+
end)
|
|
270
277
|
|
|
271
278
|
return maid
|
|
272
279
|
end)
|
|
@@ -275,21 +282,23 @@ function DataStoreStage:Observe(key, defaultValue)
|
|
|
275
282
|
return Observable.new(function(sub)
|
|
276
283
|
local maid = Maid.new()
|
|
277
284
|
|
|
278
|
-
maid:GiveTask(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
+
maid:GiveTask(
|
|
286
|
+
self._keySubscriptions:Observe(key):Subscribe(function(value)
|
|
287
|
+
if value == nil then
|
|
288
|
+
sub:Fire(defaultValue)
|
|
289
|
+
else
|
|
290
|
+
sub:Fire(value)
|
|
291
|
+
end
|
|
292
|
+
end),
|
|
293
|
+
sub:GetFailComplete()
|
|
294
|
+
)
|
|
285
295
|
|
|
286
296
|
-- Load initially
|
|
287
|
-
maid:GivePromise(self:Load(key, defaultValue))
|
|
288
|
-
:
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
end)
|
|
297
|
+
maid:GivePromise(self:Load(key, defaultValue)):Then(function(value)
|
|
298
|
+
sub:Fire(value)
|
|
299
|
+
end, function(...)
|
|
300
|
+
sub:Fail(...)
|
|
301
|
+
end)
|
|
293
302
|
|
|
294
303
|
return maid
|
|
295
304
|
end)
|
|
@@ -301,7 +310,7 @@ end
|
|
|
301
310
|
@param callback function -- May return a promise
|
|
302
311
|
@return function -- Call to remove
|
|
303
312
|
]=]
|
|
304
|
-
function DataStoreStage:AddSavingCallback(callback)
|
|
313
|
+
function DataStoreStage:AddSavingCallback(callback: () -> ())
|
|
305
314
|
assert(type(callback) == "function", "Bad callback")
|
|
306
315
|
|
|
307
316
|
table.insert(self._savingCallbacks, callback)
|
|
@@ -317,7 +326,7 @@ end
|
|
|
317
326
|
Removes a saving callback from the data store stage
|
|
318
327
|
@param callback function
|
|
319
328
|
]=]
|
|
320
|
-
function DataStoreStage:RemoveSavingCallback(callback)
|
|
329
|
+
function DataStoreStage:RemoveSavingCallback(callback: () -> ())
|
|
321
330
|
assert(type(callback) == "function", "Bad callback")
|
|
322
331
|
|
|
323
332
|
local index = table.find(self._savingCallbacks, callback)
|
|
@@ -361,10 +370,10 @@ function DataStoreStage:PromiseKeyList()
|
|
|
361
370
|
return self:PromiseKeySet()
|
|
362
371
|
:Then(function(keys)
|
|
363
372
|
local list = {}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
373
|
+
for key, _ in keys do
|
|
374
|
+
table.insert(list, key)
|
|
375
|
+
end
|
|
376
|
+
return list
|
|
368
377
|
end)
|
|
369
378
|
end
|
|
370
379
|
|
|
@@ -437,11 +446,12 @@ function DataStoreStage:MarkDataAsSaved(parentWriter)
|
|
|
437
446
|
newBaseDataSnapshot = {}
|
|
438
447
|
end
|
|
439
448
|
|
|
440
|
-
for key, value in
|
|
449
|
+
for key, value in dataToSave do
|
|
441
450
|
local ourSnapshotValue = self._saveDataSnapshot[key]
|
|
442
451
|
if Table.deepEquivalent(ourSnapshotValue, value) or ourSnapshotValue == nil then
|
|
443
452
|
-- This shouldn't fire any event because our save data is matching
|
|
444
|
-
newBaseDataSnapshot[key] =
|
|
453
|
+
newBaseDataSnapshot[key] =
|
|
454
|
+
self:_updateStoresAndComputeBaseDataSnapshotValueFromDiffSnapshot(key, value)
|
|
445
455
|
newSaveSnapshot[key] = nil
|
|
446
456
|
end
|
|
447
457
|
end
|
|
@@ -512,12 +522,12 @@ function DataStoreStage:Overwrite(data)
|
|
|
512
522
|
local newSaveSnapshot = {}
|
|
513
523
|
|
|
514
524
|
local remaining = Set.fromKeys(self._stores)
|
|
515
|
-
for key, store in
|
|
525
|
+
for key, store in self._stores do
|
|
516
526
|
-- Update each store
|
|
517
527
|
store:Overwrite(data[key])
|
|
518
528
|
end
|
|
519
529
|
|
|
520
|
-
for key, value in
|
|
530
|
+
for key, value in data do
|
|
521
531
|
remaining[key] = nil
|
|
522
532
|
if self._stores[key] then
|
|
523
533
|
self._stores[key]:Overwrite(value)
|
|
@@ -526,13 +536,13 @@ function DataStoreStage:Overwrite(data)
|
|
|
526
536
|
end
|
|
527
537
|
end
|
|
528
538
|
|
|
529
|
-
for key, _ in
|
|
539
|
+
for key, _ in remaining do
|
|
530
540
|
self._stores[key]:Overwrite(DataStoreDeleteToken)
|
|
531
541
|
end
|
|
532
542
|
|
|
533
543
|
self._saveDataSnapshot = table.freeze(newSaveSnapshot)
|
|
534
544
|
else
|
|
535
|
-
for _, store in
|
|
545
|
+
for _, store in self._stores do
|
|
536
546
|
store:Overwrite(DataStoreDeleteToken)
|
|
537
547
|
end
|
|
538
548
|
|
|
@@ -559,7 +569,7 @@ function DataStoreStage:OverwriteMerge(data)
|
|
|
559
569
|
|
|
560
570
|
if type(data) == "table" and data ~= DataStoreDeleteToken then
|
|
561
571
|
-- Note we explicitly don't wipe values here! Need delete token if we want to delete!
|
|
562
|
-
for key, value in
|
|
572
|
+
for key, value in data do
|
|
563
573
|
self:_storeAtKey(key, value)
|
|
564
574
|
end
|
|
565
575
|
else
|
|
@@ -605,7 +615,7 @@ function DataStoreStage:HasWritableData()
|
|
|
605
615
|
return true
|
|
606
616
|
end
|
|
607
617
|
|
|
608
|
-
for name, store in
|
|
618
|
+
for name, store in self._stores do
|
|
609
619
|
if not store.Destroy then
|
|
610
620
|
warn(string.format("[DataStoreStage] - Substore %q destroyed", name))
|
|
611
621
|
continue
|
|
@@ -639,7 +649,7 @@ function DataStoreStage:GetNewWriter()
|
|
|
639
649
|
writer:SetSaveDataSnapshot(self._saveDataSnapshot)
|
|
640
650
|
end
|
|
641
651
|
|
|
642
|
-
for key, store in
|
|
652
|
+
for key, store in self._stores do
|
|
643
653
|
if not store.Destroy then
|
|
644
654
|
warn(string.format("[DataStoreStage] - Substore %q destroyed", key))
|
|
645
655
|
continue
|
|
@@ -667,14 +677,14 @@ end
|
|
|
667
677
|
function DataStoreStage:PromiseInvokeSavingCallbacks()
|
|
668
678
|
local removingPromises = {}
|
|
669
679
|
|
|
670
|
-
for _, func in
|
|
680
|
+
for _, func in self._savingCallbacks do
|
|
671
681
|
local result = func()
|
|
672
682
|
if Promise.isPromise(result) then
|
|
673
683
|
table.insert(removingPromises, result)
|
|
674
684
|
end
|
|
675
685
|
end
|
|
676
686
|
|
|
677
|
-
for _, substore in
|
|
687
|
+
for _, substore in self._stores do
|
|
678
688
|
local promise = substore:PromiseInvokeSavingCallbacks()
|
|
679
689
|
if promise then
|
|
680
690
|
table.insert(removingPromises, promise)
|
|
@@ -695,7 +705,7 @@ function DataStoreStage:_createFullBaseDataSnapshot()
|
|
|
695
705
|
newSnapshot = {}
|
|
696
706
|
end
|
|
697
707
|
|
|
698
|
-
for key, store in
|
|
708
|
+
for key, store in self._stores do
|
|
699
709
|
if not store.Destroy then
|
|
700
710
|
warn(string.format("[DataStoreStage] - Substore %q destroyed", key))
|
|
701
711
|
continue
|
|
@@ -730,7 +740,7 @@ function DataStoreStage:_updateStoresAndComputeBaseDataSnapshotFromDiffSnapshot(
|
|
|
730
740
|
end
|
|
731
741
|
|
|
732
742
|
-- Merge all of our newly downloaded data here into our base layer.
|
|
733
|
-
for key, value in
|
|
743
|
+
for key, value in diffSnapshot do
|
|
734
744
|
newBaseDataSnapshot[key] = self:_updateStoresAndComputeBaseDataSnapshotValueFromDiffSnapshot(key, value)
|
|
735
745
|
end
|
|
736
746
|
|
|
@@ -750,7 +760,11 @@ function DataStoreStage:_updateStoresAndComputeBaseDataSnapshotValueFromDiffSnap
|
|
|
750
760
|
return nil
|
|
751
761
|
elseif value == DataStoreDeleteToken then
|
|
752
762
|
return nil
|
|
753
|
-
elseif
|
|
763
|
+
elseif
|
|
764
|
+
type(value) == "table"
|
|
765
|
+
and type(self._baseDataSnapshot) == "table"
|
|
766
|
+
and type(self._baseDataSnapshot[key]) == "table"
|
|
767
|
+
then
|
|
754
768
|
return self:_recurseMergeTable(self._baseDataSnapshot[key], value)
|
|
755
769
|
else
|
|
756
770
|
return value
|
|
@@ -765,7 +779,7 @@ function DataStoreStage:_recurseMergeTable(original, incoming)
|
|
|
765
779
|
local newSnapshot = table.clone(original)
|
|
766
780
|
|
|
767
781
|
-- Overwerite with merged values...
|
|
768
|
-
for key, value in
|
|
782
|
+
for key, value in incoming do
|
|
769
783
|
newSnapshot[key] = self:_recurseMergeTable(original[key], value)
|
|
770
784
|
end
|
|
771
785
|
|
|
@@ -788,11 +802,11 @@ function DataStoreStage:_updateViewSnapshot()
|
|
|
788
802
|
local changedKeys = self:_computeChangedKeys(previousViewSnapshot, newViewSnapshot)
|
|
789
803
|
if next(changedKeys) ~= nil then
|
|
790
804
|
if type(newViewSnapshot) == "table" then
|
|
791
|
-
for key, _ in
|
|
805
|
+
for key, _ in changedKeys do
|
|
792
806
|
self._keySubscriptions:Fire(key, newViewSnapshot[key])
|
|
793
807
|
end
|
|
794
808
|
else
|
|
795
|
-
for key, _ in
|
|
809
|
+
for key, _ in changedKeys do
|
|
796
810
|
self._keySubscriptions:Fire(key, nil)
|
|
797
811
|
end
|
|
798
812
|
end
|
|
@@ -810,7 +824,7 @@ function DataStoreStage:_computeChangedKeys(previousViewSnapshot, newViewSnapsho
|
|
|
810
824
|
local changedKeys = {}
|
|
811
825
|
|
|
812
826
|
local keys = Set.union(Set.fromKeys(previousViewSnapshot), Set.fromKeys(newViewSnapshot))
|
|
813
|
-
for key, _ in
|
|
827
|
+
for key, _ in keys do
|
|
814
828
|
if not Table.deepEquivalent(previousViewSnapshot[key], newViewSnapshot[key]) then
|
|
815
829
|
changedKeys[key] = true
|
|
816
830
|
end
|
|
@@ -869,13 +883,13 @@ function DataStoreStage:_computeNewViewSnapshot()
|
|
|
869
883
|
end
|
|
870
884
|
|
|
871
885
|
-- Add in stores
|
|
872
|
-
for key, store in
|
|
886
|
+
for key, store in self._stores do
|
|
873
887
|
newView[key] = store._viewSnapshot
|
|
874
888
|
end
|
|
875
889
|
|
|
876
890
|
-- Then finally save data
|
|
877
891
|
if type(self._saveDataSnapshot) == "table" then
|
|
878
|
-
for key, value in
|
|
892
|
+
for key, value in self._saveDataSnapshot do
|
|
879
893
|
if value == DataStoreDeleteToken then
|
|
880
894
|
newView[key] = nil
|
|
881
895
|
else
|
|
@@ -884,7 +898,10 @@ function DataStoreStage:_computeNewViewSnapshot()
|
|
|
884
898
|
end
|
|
885
899
|
end
|
|
886
900
|
|
|
887
|
-
if
|
|
901
|
+
if
|
|
902
|
+
next(newView) == nil
|
|
903
|
+
and not (type(self._baseDataSnapshot) == "table" or type(self._saveDataSnapshot) == "table")
|
|
904
|
+
then
|
|
888
905
|
-- We have no reason to be a table, make sure we return nil
|
|
889
906
|
return nil
|
|
890
907
|
end
|
|
@@ -995,11 +1012,11 @@ function DataStoreStage:_checkSnapshotIntegrity(label, result)
|
|
|
995
1012
|
return
|
|
996
1013
|
end
|
|
997
1014
|
|
|
998
|
-
for key, item in
|
|
1015
|
+
for key, item in value do
|
|
999
1016
|
local keyLabel = innerLabel .. "." .. tostring(key)
|
|
1000
1017
|
|
|
1001
1018
|
if not (type(key) == "string" or type(key) == "number") then
|
|
1002
|
-
|
|
1019
|
+
error(string.format("%s should be a number or string", keyLabel))
|
|
1003
1020
|
end
|
|
1004
1021
|
|
|
1005
1022
|
if item == DataStoreDeleteToken then
|
|
@@ -1037,7 +1054,7 @@ function DataStoreStage:_checkIntegrity()
|
|
|
1037
1054
|
self:_checkSnapshotIntegrity("self._viewSnapshot", self._viewSnapshot)
|
|
1038
1055
|
end
|
|
1039
1056
|
|
|
1040
|
-
for key, _ in
|
|
1057
|
+
for key, _ in self._stores do
|
|
1041
1058
|
if type(self._baseDataSnapshot) == "table" and self._baseDataSnapshot[key] ~= nil then
|
|
1042
1059
|
error(string.format("[DataStoreStage] - Duplicate baseData at key %q", key))
|
|
1043
1060
|
end
|
|
@@ -24,7 +24,7 @@ DataStoreWriter.__index = DataStoreWriter
|
|
|
24
24
|
@param debugName string
|
|
25
25
|
@return DataStoreWriter
|
|
26
26
|
]=]
|
|
27
|
-
function DataStoreWriter.new(debugName)
|
|
27
|
+
function DataStoreWriter.new(debugName: string)
|
|
28
28
|
local self = setmetatable({}, DataStoreWriter)
|
|
29
29
|
|
|
30
30
|
self._debugName = assert(debugName, "No debugName")
|
|
@@ -66,7 +66,10 @@ function DataStoreWriter:GetSubWritersMap()
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
function DataStoreWriter:SetFullBaseDataSnapshot(fullBaseDataSnapshot)
|
|
69
|
-
assert(
|
|
69
|
+
assert(
|
|
70
|
+
type(fullBaseDataSnapshot) ~= "table" or table.isfrozen(fullBaseDataSnapshot),
|
|
71
|
+
"fullBaseDataSnapshot should be frozen"
|
|
72
|
+
)
|
|
70
73
|
assert(not Symbol.isSymbol(fullBaseDataSnapshot), "fullBaseDataSnapshot should not be symbol")
|
|
71
74
|
|
|
72
75
|
if fullBaseDataSnapshot == DataStoreDeleteToken then
|
|
@@ -81,7 +84,7 @@ end
|
|
|
81
84
|
@param name string
|
|
82
85
|
@param writer DataStoreWriter
|
|
83
86
|
]=]
|
|
84
|
-
function DataStoreWriter:AddSubWriter(name, writer)
|
|
87
|
+
function DataStoreWriter:AddSubWriter(name: string, writer)
|
|
85
88
|
assert(type(name) == "string", "Bad name")
|
|
86
89
|
assert(not self._writers[name], "Writer already exists for name")
|
|
87
90
|
assert(writer, "Bad writer")
|
|
@@ -95,7 +98,7 @@ end
|
|
|
95
98
|
@param name string
|
|
96
99
|
@return DataStoreWriter
|
|
97
100
|
]=]
|
|
98
|
-
function DataStoreWriter:GetWriter(name)
|
|
101
|
+
function DataStoreWriter:GetWriter(name: string)
|
|
99
102
|
assert(type(name) == "string", "Bad name")
|
|
100
103
|
|
|
101
104
|
return self._writers[name]
|
|
@@ -124,7 +127,7 @@ function DataStoreWriter:ComputeDiffSnapshot(incoming)
|
|
|
124
127
|
end
|
|
125
128
|
|
|
126
129
|
local diffSnapshot = {}
|
|
127
|
-
for key, _ in
|
|
130
|
+
for key, _ in keys do
|
|
128
131
|
if self._writers[key] then
|
|
129
132
|
diffSnapshot[key] = self._writers[key]:ComputeDiffSnapshot(incoming[key])
|
|
130
133
|
else
|
|
@@ -172,7 +175,7 @@ function DataStoreWriter:_computeTableDiff(original, incoming)
|
|
|
172
175
|
local keys = Set.union(Set.fromKeys(original), Set.fromKeys(incoming))
|
|
173
176
|
|
|
174
177
|
local diffSnapshot = {}
|
|
175
|
-
for key, _ in
|
|
178
|
+
for key, _ in keys do
|
|
176
179
|
diffSnapshot[key] = self:_computeValueDiff(original[key], incoming[key])
|
|
177
180
|
end
|
|
178
181
|
|
|
@@ -224,7 +227,7 @@ function DataStoreWriter:_writeMergeWriters(original)
|
|
|
224
227
|
end
|
|
225
228
|
|
|
226
229
|
-- Write our writers first...
|
|
227
|
-
for key, writer in
|
|
230
|
+
for key, writer in self._writers do
|
|
228
231
|
local result = writer:WriteMerge(copy[key])
|
|
229
232
|
if result == DataStoreDeleteToken then
|
|
230
233
|
copy[key] = nil
|
|
@@ -235,15 +238,26 @@ function DataStoreWriter:_writeMergeWriters(original)
|
|
|
235
238
|
end
|
|
236
239
|
|
|
237
240
|
-- Write our save data next
|
|
238
|
-
if
|
|
241
|
+
if
|
|
242
|
+
not Symbol.isSymbol(self._saveDataSnapshot)
|
|
243
|
+
and type(self._saveDataSnapshot) == "table"
|
|
244
|
+
and next(self._saveDataSnapshot) ~= nil
|
|
245
|
+
then
|
|
239
246
|
-- Original was not a table. We need to swap to one.
|
|
240
247
|
if type(copy) ~= "table" then
|
|
241
248
|
copy = {}
|
|
242
249
|
end
|
|
243
250
|
|
|
244
|
-
for key, value in
|
|
251
|
+
for key, value in self._saveDataSnapshot do
|
|
245
252
|
if self._writers[key] then
|
|
246
|
-
warn(
|
|
253
|
+
warn(
|
|
254
|
+
string.format(
|
|
255
|
+
"[DataStoreWriter._writeMergeWriters] - Overwriting key %q already saved as rawData with a writer with %q (was %q)",
|
|
256
|
+
key,
|
|
257
|
+
tostring(value),
|
|
258
|
+
tostring(copy[key])
|
|
259
|
+
)
|
|
260
|
+
)
|
|
247
261
|
end
|
|
248
262
|
|
|
249
263
|
if value == DataStoreDeleteToken then
|
|
@@ -276,7 +290,11 @@ function DataStoreWriter:WriteMerge(original)
|
|
|
276
290
|
|
|
277
291
|
if self._saveDataSnapshot == DataStoreDeleteToken then
|
|
278
292
|
return DataStoreDeleteToken
|
|
279
|
-
elseif
|
|
293
|
+
elseif
|
|
294
|
+
self._saveDataSnapshot == UNSET_TOKEN
|
|
295
|
+
or self._saveDataSnapshot == nil
|
|
296
|
+
or type(self._saveDataSnapshot) == "table"
|
|
297
|
+
then
|
|
280
298
|
return self:_writeMergeWriters(original)
|
|
281
299
|
else
|
|
282
300
|
-- Save data must be a boolean or something
|
|
@@ -284,7 +302,7 @@ function DataStoreWriter:WriteMerge(original)
|
|
|
284
302
|
end
|
|
285
303
|
end
|
|
286
304
|
|
|
287
|
-
function DataStoreWriter:IsCompleteWipe()
|
|
305
|
+
function DataStoreWriter:IsCompleteWipe(): boolean
|
|
288
306
|
if self._saveDataSnapshot == UNSET_TOKEN then
|
|
289
307
|
return false
|
|
290
308
|
end
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
local topMaid = Maid.new()
|
|
19
19
|
|
|
20
|
-
local function handlePlayer(player)
|
|
20
|
+
local function handlePlayer(player: Player)
|
|
21
21
|
local maid = Maid.new()
|
|
22
22
|
|
|
23
23
|
local playerMoneyValue = Instance.new("IntValue")
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
Players.PlayerRemoving:Connect(function(player)
|
|
40
40
|
topMaid[player] = nil
|
|
41
41
|
end)
|
|
42
|
-
for _, player in
|
|
42
|
+
for _, player in Players:GetPlayers() do
|
|
43
43
|
task.spawn(handlePlayer, player)
|
|
44
44
|
end
|
|
45
45
|
```
|
|
@@ -72,7 +72,7 @@ PlayerDataStoreManager.__index = PlayerDataStoreManager
|
|
|
72
72
|
@param skipBindingToClose boolean?
|
|
73
73
|
@return PlayerDataStoreManager
|
|
74
74
|
]=]
|
|
75
|
-
function PlayerDataStoreManager.new(robloxDataStore, keyGenerator, skipBindingToClose)
|
|
75
|
+
function PlayerDataStoreManager.new(robloxDataStore: DataStore, keyGenerator, skipBindingToClose)
|
|
76
76
|
local self = setmetatable(BaseObject.new(), PlayerDataStoreManager)
|
|
77
77
|
|
|
78
78
|
assert(type(skipBindingToClose) == "boolean" or skipBindingToClose == nil, "Bad skipBindingToClose")
|
|
@@ -131,7 +131,7 @@ end
|
|
|
131
131
|
|
|
132
132
|
@param player Player
|
|
133
133
|
]=]
|
|
134
|
-
function PlayerDataStoreManager:RemovePlayerDataStore(player)
|
|
134
|
+
function PlayerDataStoreManager:RemovePlayerDataStore(player: Player)
|
|
135
135
|
self:_removePlayerDataStore(player)
|
|
136
136
|
end
|
|
137
137
|
|
|
@@ -139,7 +139,7 @@ end
|
|
|
139
139
|
@param player Player
|
|
140
140
|
@return DataStore
|
|
141
141
|
]=]
|
|
142
|
-
function PlayerDataStoreManager:GetDataStore(player)
|
|
142
|
+
function PlayerDataStoreManager:GetDataStore(player: Player)
|
|
143
143
|
assert(typeof(player) == "Instance", "Bad player")
|
|
144
144
|
assert(player:IsA("Player"), "Bad player")
|
|
145
145
|
|
|
@@ -161,13 +161,13 @@ end
|
|
|
161
161
|
@return Promise
|
|
162
162
|
]=]
|
|
163
163
|
function PlayerDataStoreManager:PromiseAllSaves()
|
|
164
|
-
for player, _ in
|
|
164
|
+
for player, _ in self._datastores do
|
|
165
165
|
self:_removePlayerDataStore(player)
|
|
166
166
|
end
|
|
167
167
|
return self._maid:GivePromise(PromiseUtils.all(self._pendingSaves:GetAll()))
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
-
function PlayerDataStoreManager:_createDataStore(player)
|
|
170
|
+
function PlayerDataStoreManager:_createDataStore(player: Player)
|
|
171
171
|
assert(not self._datastores[player], "Bad player")
|
|
172
172
|
|
|
173
173
|
local datastore = DataStore.new(self._robloxDataStore, self:_getKey(player))
|
|
@@ -182,7 +182,7 @@ function PlayerDataStoreManager:_createDataStore(player)
|
|
|
182
182
|
return datastore
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
-
function PlayerDataStoreManager:_removePlayerDataStore(player)
|
|
185
|
+
function PlayerDataStoreManager:_removePlayerDataStore(player: Player)
|
|
186
186
|
assert(typeof(player) == "Instance", "Bad player")
|
|
187
187
|
assert(player:IsA("Player"), "Bad player")
|
|
188
188
|
|
|
@@ -194,7 +194,7 @@ function PlayerDataStoreManager:_removePlayerDataStore(player)
|
|
|
194
194
|
self._removing[player] = true
|
|
195
195
|
|
|
196
196
|
local removingPromises = {}
|
|
197
|
-
for _, func in
|
|
197
|
+
for _, func in self._removingCallbacks do
|
|
198
198
|
local result = func(player)
|
|
199
199
|
if Promise.isPromise(result) then
|
|
200
200
|
table.insert(removingPromises, result)
|
|
@@ -215,7 +215,7 @@ function PlayerDataStoreManager:_removePlayerDataStore(player)
|
|
|
215
215
|
self._maid._savingConns[player] = nil
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
-
function PlayerDataStoreManager:_getKey(player)
|
|
218
|
+
function PlayerDataStoreManager:_getKey(player: Player)
|
|
219
219
|
return self._keyGenerator(player)
|
|
220
220
|
end
|
|
221
221
|
|
|
@@ -11,6 +11,7 @@ local PlayerDataStoreManager = require("PlayerDataStoreManager")
|
|
|
11
11
|
local DataStorePromises = require("DataStorePromises")
|
|
12
12
|
local Promise = require("Promise")
|
|
13
13
|
local Maid = require("Maid")
|
|
14
|
+
local _ServiceBag = require("ServiceBag")
|
|
14
15
|
|
|
15
16
|
local PlayerDataStoreService = {}
|
|
16
17
|
PlayerDataStoreService.ServiceName = "PlayerDataStoreService"
|
|
@@ -19,7 +20,7 @@ PlayerDataStoreService.ServiceName = "PlayerDataStoreService"
|
|
|
19
20
|
Initializes the PlayerDataStoreService. Should be done via [ServiceBag.Init].
|
|
20
21
|
@param serviceBag ServiceBag
|
|
21
22
|
]=]
|
|
22
|
-
function PlayerDataStoreService:Init(serviceBag)
|
|
23
|
+
function PlayerDataStoreService:Init(serviceBag: _ServiceBag.ServiceBag)
|
|
23
24
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
24
25
|
self._maid = Maid.new()
|
|
25
26
|
|
|
@@ -49,7 +50,7 @@ end
|
|
|
49
50
|
|
|
50
51
|
@param dataStoreName string
|
|
51
52
|
]=]
|
|
52
|
-
function PlayerDataStoreService:SetDataStoreName(dataStoreName)
|
|
53
|
+
function PlayerDataStoreService:SetDataStoreName(dataStoreName: string)
|
|
53
54
|
assert(type(dataStoreName) == "string", "Bad dataStoreName")
|
|
54
55
|
assert(self._promiseStarted, "Not initialized")
|
|
55
56
|
assert(self._promiseStarted:IsPending(), "Already started, cannot configure")
|
|
@@ -66,7 +67,7 @@ end
|
|
|
66
67
|
|
|
67
68
|
@param dataStoreScope string
|
|
68
69
|
]=]
|
|
69
|
-
function PlayerDataStoreService:SetDataStoreScope(dataStoreScope)
|
|
70
|
+
function PlayerDataStoreService:SetDataStoreScope(dataStoreScope: string)
|
|
70
71
|
assert(type(dataStoreScope) == "string", "Bad dataStoreScope")
|
|
71
72
|
assert(self._promiseStarted, "Not initialized")
|
|
72
73
|
assert(self._promiseStarted:IsPending(), "Already started, cannot configure")
|
|
@@ -10,11 +10,12 @@ local require = require(script.Parent.loader).load(script)
|
|
|
10
10
|
local DataStore = require("DataStore")
|
|
11
11
|
local DataStorePromises = require("DataStorePromises")
|
|
12
12
|
local Maid = require("Maid")
|
|
13
|
+
local _ServiceBag = require("ServiceBag")
|
|
13
14
|
|
|
14
15
|
local PrivateServerDataStoreService = {}
|
|
15
16
|
PrivateServerDataStoreService.ServiceName = "PrivateServerDataStoreService"
|
|
16
17
|
|
|
17
|
-
function PrivateServerDataStoreService:Init(serviceBag)
|
|
18
|
+
function PrivateServerDataStoreService:Init(serviceBag: _ServiceBag.ServiceBag)
|
|
18
19
|
assert(not self._serviceBag, "Already initialized")
|
|
19
20
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
20
21
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Utility methods to interactive with Roblox datastores.
|
|
3
4
|
@server
|
|
@@ -21,7 +22,7 @@ local DataStorePromises = {}
|
|
|
21
22
|
@param scope string
|
|
22
23
|
@return Promise<DataStore>
|
|
23
24
|
]=]
|
|
24
|
-
function DataStorePromises.promiseDataStore(name, scope)
|
|
25
|
+
function DataStorePromises.promiseDataStore(name: string, scope: string): Promise.Promise<DataStore>
|
|
25
26
|
assert(type(name) == "string", "Bad name")
|
|
26
27
|
assert(type(scope) == "string", "Bad scope")
|
|
27
28
|
|
|
@@ -44,7 +45,7 @@ end
|
|
|
44
45
|
@param scope string
|
|
45
46
|
@return Promise<OrderedDataStore>
|
|
46
47
|
]=]
|
|
47
|
-
function DataStorePromises.promiseOrderedDataStore(name, scope)
|
|
48
|
+
function DataStorePromises.promiseOrderedDataStore(name: string, scope: string): Promise.Promise<OrderedDataStore>
|
|
48
49
|
assert(type(name) == "string", "Bad name")
|
|
49
50
|
assert(type(scope) == "string", "Bad scope")
|
|
50
51
|
|
|
@@ -66,7 +67,7 @@ end
|
|
|
66
67
|
@param key string
|
|
67
68
|
@return Promise<T>
|
|
68
69
|
]=]
|
|
69
|
-
function DataStorePromises.getAsync(robloxDataStore, key)
|
|
70
|
+
function DataStorePromises.getAsync<T>(robloxDataStore: DataStore, key: string): Promise.Promise<T>
|
|
70
71
|
assert(typeof(robloxDataStore) == "Instance", "Bad robloxDataStore")
|
|
71
72
|
assert(type(key) == "string", "Bad key")
|
|
72
73
|
|
|
@@ -88,10 +89,14 @@ end
|
|
|
88
89
|
@param robloxDataStore DataStore
|
|
89
90
|
@param key string
|
|
90
91
|
@param updateFunc (T) -> T?
|
|
91
|
-
@return Promise<
|
|
92
|
+
@return Promise<T>
|
|
92
93
|
]=]
|
|
93
94
|
|
|
94
|
-
function DataStorePromises.updateAsync(
|
|
95
|
+
function DataStorePromises.updateAsync<T>(
|
|
96
|
+
robloxDataStore: DataStore,
|
|
97
|
+
key: string,
|
|
98
|
+
updateFunc: (T) -> T?
|
|
99
|
+
): Promise.Promise<(T, DataStoreKeyInfo)>
|
|
95
100
|
assert(typeof(robloxDataStore) == "Instance", "Bad robloxDataStore")
|
|
96
101
|
assert(type(key) == "string", "Bad key")
|
|
97
102
|
assert(type(updateFunc) == "function", "Bad updateFunc")
|
|
@@ -119,7 +124,12 @@ end
|
|
|
119
124
|
@param userIds { number } -- Associated userIds
|
|
120
125
|
@return Promise<boolean>
|
|
121
126
|
]=]
|
|
122
|
-
function DataStorePromises.setAsync(
|
|
127
|
+
function DataStorePromises.setAsync(
|
|
128
|
+
robloxDataStore: DataStore,
|
|
129
|
+
key,
|
|
130
|
+
value: string,
|
|
131
|
+
userIds: { number }?
|
|
132
|
+
): Promise.Promise<boolean>
|
|
123
133
|
assert(typeof(robloxDataStore) == "Instance", "Bad robloxDataStore")
|
|
124
134
|
assert(type(key) == "string", "Bad key")
|
|
125
135
|
assert(type(userIds) == "table" or userIds == nil, "Bad userIds")
|
|
@@ -142,7 +152,11 @@ end
|
|
|
142
152
|
@param delta number
|
|
143
153
|
@return Promise<boolean>
|
|
144
154
|
]=]
|
|
145
|
-
function DataStorePromises.promiseIncrementAsync(
|
|
155
|
+
function DataStorePromises.promiseIncrementAsync(
|
|
156
|
+
robloxDataStore: DataStore,
|
|
157
|
+
key: string,
|
|
158
|
+
delta: number
|
|
159
|
+
): Promise.Promise<boolean>
|
|
146
160
|
assert(typeof(robloxDataStore) == "Instance", "Bad robloxDataStore")
|
|
147
161
|
assert(type(key) == "string", "Bad key")
|
|
148
162
|
assert(type(delta) == "number" or delta == nil, "Bad delta")
|
|
@@ -164,7 +178,7 @@ end
|
|
|
164
178
|
@param key string
|
|
165
179
|
@return Promise<boolean>
|
|
166
180
|
]=]
|
|
167
|
-
function DataStorePromises.removeAsync(robloxDataStore, key)
|
|
181
|
+
function DataStorePromises.removeAsync(robloxDataStore: DataStore, key: string): Promise.Promise<boolean>
|
|
168
182
|
assert(typeof(robloxDataStore) == "Instance", "Bad robloxDataStore")
|
|
169
183
|
assert(type(key) == "string", "Bad key")
|
|
170
184
|
|
|
@@ -191,7 +205,13 @@ end
|
|
|
191
205
|
@param maxValue number?
|
|
192
206
|
@return Promise<DataStorePages>
|
|
193
207
|
]=]
|
|
194
|
-
function DataStorePromises.promiseSortedPagesAsync(
|
|
208
|
+
function DataStorePromises.promiseSortedPagesAsync(
|
|
209
|
+
orderedDataStore: OrderedDataStore,
|
|
210
|
+
ascending: boolean,
|
|
211
|
+
pagesize: number,
|
|
212
|
+
minValue: number?,
|
|
213
|
+
maxValue: number?
|
|
214
|
+
): Promise.Promise<DataStorePages>
|
|
195
215
|
assert(typeof(orderedDataStore) == "Instance" and orderedDataStore:IsA("OrderedDataStore"), "Bad orderedDataStore")
|
|
196
216
|
assert(type(ascending) == "boolean", "Bad ascending")
|
|
197
217
|
assert(type(pagesize) == "number", "Bad entries")
|
|
@@ -212,7 +232,6 @@ function DataStorePromises.promiseSortedPagesAsync(orderedDataStore, ascending,
|
|
|
212
232
|
end)
|
|
213
233
|
end
|
|
214
234
|
|
|
215
|
-
|
|
216
235
|
--[=[
|
|
217
236
|
@interface OrderedDataStoreEntry
|
|
218
237
|
.key any
|
|
@@ -253,19 +272,20 @@ end
|
|
|
253
272
|
@param maxValue number?
|
|
254
273
|
@return Promise<OrderedDataStoreEntry>
|
|
255
274
|
]=]
|
|
256
|
-
function DataStorePromises.promiseOrderedEntries(orderedDataStore, ascending, pagesize, entries, minValue
|
|
275
|
+
function DataStorePromises.promiseOrderedEntries(orderedDataStore: OrderedDataStore, ascending: boolean, pagesize: number, entries: number, minValue: number?, maxValue: number?): Promise.Promise<OrderedDataStoreEntry>
|
|
257
276
|
assert(typeof(orderedDataStore) == "Instance" and orderedDataStore:IsA("OrderedDataStore"), "Bad orderedDataStore")
|
|
258
277
|
assert(type(ascending) == "boolean", "Bad ascending")
|
|
259
278
|
assert(type(entries) == "number", "Bad entries")
|
|
260
279
|
|
|
280
|
+
-- stylua: ignore
|
|
261
281
|
return DataStorePromises.promiseSortedPagesAsync(orderedDataStore, ascending, pagesize, minValue, maxValue)
|
|
262
|
-
:Then(function(dataStorePages)
|
|
282
|
+
:Then(function(dataStorePages: DataStorePages)
|
|
263
283
|
return Promise.spawn(function(resolve, reject)
|
|
264
284
|
local resultList = {}
|
|
265
285
|
|
|
266
|
-
local pageData = dataStorePages:GetCurrentPage()
|
|
286
|
+
local pageData: any? = dataStorePages:GetCurrentPage()
|
|
267
287
|
while pageData do
|
|
268
|
-
for _, data in
|
|
288
|
+
for _, data in pageData do
|
|
269
289
|
if #resultList < entries then
|
|
270
290
|
table.insert(resultList, data)
|
|
271
291
|
else
|