@quenty/datastore 13.20.2 → 13.20.3
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 +8 -0
- package/package.json +13 -13
- package/src/Server/DataStore.lua +97 -68
- package/src/Server/Modules/DataStoreStage.lua +87 -70
- package/src/Server/Modules/DataStoreWriter.lua +33 -19
- package/src/Server/PlayerDataStoreManager.lua +13 -0
- package/src/Server/PlayerDataStoreService.lua +11 -0
- package/src/Server/Utility/DataStorePromises.lua +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
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.3](https://github.com/Quenty/NevermoreEngine/compare/@quenty/datastore@13.20.2...@quenty/datastore@13.20.3) (2025-04-10)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @quenty/datastore
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## [13.20.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/datastore@13.20.0...@quenty/datastore@13.20.2) (2025-04-07)
|
|
7
15
|
|
|
8
16
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/datastore",
|
|
3
|
-
"version": "13.20.
|
|
3
|
+
"version": "13.20.3",
|
|
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.
|
|
29
|
+
"@quenty/baseobject": "^10.8.3",
|
|
30
|
+
"@quenty/bindtocloseservice": "^8.17.3",
|
|
31
|
+
"@quenty/loader": "^10.8.3",
|
|
32
|
+
"@quenty/maid": "^3.4.3",
|
|
33
33
|
"@quenty/math": "^2.7.3",
|
|
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.
|
|
34
|
+
"@quenty/pagesutils": "^5.11.4",
|
|
35
|
+
"@quenty/promise": "^10.10.4",
|
|
36
|
+
"@quenty/rx": "^13.17.3",
|
|
37
|
+
"@quenty/servicebag": "^11.11.4",
|
|
38
|
+
"@quenty/signal": "^7.10.3",
|
|
39
39
|
"@quenty/symbol": "^3.4.2",
|
|
40
|
-
"@quenty/table": "^3.7.
|
|
41
|
-
"@quenty/valueobject": "^13.17.
|
|
40
|
+
"@quenty/table": "^3.7.4",
|
|
41
|
+
"@quenty/valueobject": "^13.17.3"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "b06c070ae91d5dab7bd8de6e290ad2caabb15d8f"
|
|
47
47
|
}
|
package/src/Server/DataStore.lua
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Wraps the datastore object to provide async cached loading and saving. See [DataStoreStage] for more API.
|
|
3
4
|
|
|
@@ -78,14 +79,28 @@ local ValueObject = require("ValueObject")
|
|
|
78
79
|
|
|
79
80
|
local DEFAULT_DEBUG_WRITING = false
|
|
80
81
|
|
|
81
|
-
local DEFAULT_AUTO_SAVE_TIME_SECONDS = 60*5
|
|
82
|
+
local DEFAULT_AUTO_SAVE_TIME_SECONDS = 60 * 5
|
|
82
83
|
local DEFAULT_JITTER_PROPORTION = 0.1 -- Randomly assign jitter so if a ton of players join at once we don't hit the datastore at once
|
|
83
84
|
|
|
84
85
|
local DataStore = setmetatable({}, DataStoreStage)
|
|
85
86
|
DataStore.ClassName = "DataStore"
|
|
86
87
|
DataStore.__index = DataStore
|
|
87
88
|
|
|
88
|
-
export type DataStore = typeof(setmetatable(
|
|
89
|
+
export type DataStore = typeof(setmetatable(
|
|
90
|
+
{} :: {
|
|
91
|
+
_key: string,
|
|
92
|
+
_userIdList: { number }?,
|
|
93
|
+
_robloxDataStore: DataStorePromises.RobloxDataStore,
|
|
94
|
+
_debugWriting: boolean,
|
|
95
|
+
_autoSaveTimeSeconds: ValueObject.ValueObject<number?>,
|
|
96
|
+
_jitterProportion: ValueObject.ValueObject<number>,
|
|
97
|
+
_syncOnSave: ValueObject.ValueObject<boolean>,
|
|
98
|
+
_loadedOk: ValueObject.ValueObject<boolean>,
|
|
99
|
+
_firstLoadPromise: Promise.Promise<()>,
|
|
100
|
+
Saving: Signal.Signal<Promise.Promise<()>>,
|
|
101
|
+
},
|
|
102
|
+
{} :: typeof({ __index = DataStore })
|
|
103
|
+
)) & DataStoreStage.DataStoreStage
|
|
89
104
|
|
|
90
105
|
--[=[
|
|
91
106
|
Constructs a new DataStore. See [DataStoreStage] for more API.
|
|
@@ -98,14 +113,14 @@ export type DataStore = typeof(setmetatable({}, { __index = DataStore }))
|
|
|
98
113
|
@param key string
|
|
99
114
|
@return DataStore
|
|
100
115
|
]=]
|
|
101
|
-
function DataStore.new(robloxDataStore, key: string)
|
|
102
|
-
local self = setmetatable(DataStoreStage.new(key), DataStore)
|
|
116
|
+
function DataStore.new(robloxDataStore: DataStorePromises.RobloxDataStore, key: string)
|
|
117
|
+
local self: DataStore = setmetatable(DataStoreStage.new(key) :: any, DataStore)
|
|
103
118
|
|
|
104
119
|
self._key = key or error("No key")
|
|
105
120
|
self._robloxDataStore = robloxDataStore or error("No robloxDataStore")
|
|
106
121
|
self._debugWriting = DEFAULT_DEBUG_WRITING
|
|
107
122
|
|
|
108
|
-
self._autoSaveTimeSeconds = self._maid:Add(ValueObject.new(DEFAULT_AUTO_SAVE_TIME_SECONDS))
|
|
123
|
+
self._autoSaveTimeSeconds = self._maid:Add(ValueObject.new(DEFAULT_AUTO_SAVE_TIME_SECONDS :: number?))
|
|
109
124
|
self._jitterProportion = self._maid:Add(ValueObject.new(DEFAULT_JITTER_PROPORTION, "number"))
|
|
110
125
|
self._syncOnSave = self._maid:Add(ValueObject.new(false, "boolean"))
|
|
111
126
|
self._loadedOk = self._maid:Add(ValueObject.new(false, "boolean"))
|
|
@@ -121,7 +136,7 @@ function DataStore.new(robloxDataStore, key: string)
|
|
|
121
136
|
@prop Saving Signal<Promise>
|
|
122
137
|
@within DataStore
|
|
123
138
|
]=]
|
|
124
|
-
self.Saving = self._maid:Add(Signal.new()) -- :Fire(promise)
|
|
139
|
+
self.Saving = self._maid:Add(Signal.new() :: any) -- :Fire(promise)
|
|
125
140
|
|
|
126
141
|
self:_setupAutoSaving()
|
|
127
142
|
|
|
@@ -133,7 +148,7 @@ end
|
|
|
133
148
|
|
|
134
149
|
@param debugWriting boolean
|
|
135
150
|
]=]
|
|
136
|
-
function DataStore
|
|
151
|
+
function DataStore.SetDoDebugWriting(self: DataStore, debugWriting: boolean)
|
|
137
152
|
assert(type(debugWriting) == "boolean", "Bad debugWriting")
|
|
138
153
|
|
|
139
154
|
self._debugWriting = debugWriting
|
|
@@ -143,7 +158,7 @@ end
|
|
|
143
158
|
Returns the full path for the datastore
|
|
144
159
|
@return string
|
|
145
160
|
]=]
|
|
146
|
-
function DataStore
|
|
161
|
+
function DataStore.GetFullPath(self: DataStore): string
|
|
147
162
|
return string.format("RobloxDataStore@%s", self._key)
|
|
148
163
|
end
|
|
149
164
|
|
|
@@ -153,7 +168,7 @@ end
|
|
|
153
168
|
|
|
154
169
|
@param autoSaveTimeSeconds number?
|
|
155
170
|
]=]
|
|
156
|
-
function DataStore
|
|
171
|
+
function DataStore.SetAutoSaveTimeSeconds(self: DataStore, autoSaveTimeSeconds: number?)
|
|
157
172
|
assert(type(autoSaveTimeSeconds) == "number" or autoSaveTimeSeconds == nil, "Bad autoSaveTimeSeconds")
|
|
158
173
|
|
|
159
174
|
self._autoSaveTimeSeconds.Value = autoSaveTimeSeconds
|
|
@@ -164,7 +179,7 @@ end
|
|
|
164
179
|
|
|
165
180
|
@param syncEnabled boolean
|
|
166
181
|
]=]
|
|
167
|
-
function DataStore
|
|
182
|
+
function DataStore.SetSyncOnSave(self: DataStore, syncEnabled: boolean)
|
|
168
183
|
assert(type(syncEnabled) == "boolean", "Bad syncEnabled")
|
|
169
184
|
|
|
170
185
|
self._syncOnSave.Value = syncEnabled
|
|
@@ -174,7 +189,7 @@ end
|
|
|
174
189
|
Returns whether the datastore failed.
|
|
175
190
|
@return boolean
|
|
176
191
|
]=]
|
|
177
|
-
function DataStore
|
|
192
|
+
function DataStore.DidLoadFail(self: DataStore): boolean
|
|
178
193
|
if not self._firstLoadPromise then
|
|
179
194
|
return false
|
|
180
195
|
end
|
|
@@ -191,7 +206,7 @@ end
|
|
|
191
206
|
|
|
192
207
|
@return Promise<boolean>
|
|
193
208
|
]=]
|
|
194
|
-
function DataStore
|
|
209
|
+
function DataStore.PromiseLoadSuccessful(self: DataStore): Promise.Promise<boolean>
|
|
195
210
|
return self._maid:GivePromise(self:PromiseViewUpToDate()):Then(function()
|
|
196
211
|
return true
|
|
197
212
|
end, function()
|
|
@@ -203,7 +218,7 @@ end
|
|
|
203
218
|
Saves all stored data.
|
|
204
219
|
@return Promise
|
|
205
220
|
]=]
|
|
206
|
-
function DataStore
|
|
221
|
+
function DataStore.Save(self: DataStore): Promise.Promise<()>
|
|
207
222
|
return self:_syncData(false)
|
|
208
223
|
end
|
|
209
224
|
|
|
@@ -213,7 +228,7 @@ end
|
|
|
213
228
|
|
|
214
229
|
@return Promise
|
|
215
230
|
]=]
|
|
216
|
-
function DataStore
|
|
231
|
+
function DataStore.Sync(self: DataStore): Promise.Promise<()>
|
|
217
232
|
return self:_syncData(true)
|
|
218
233
|
end
|
|
219
234
|
|
|
@@ -222,7 +237,7 @@ end
|
|
|
222
237
|
|
|
223
238
|
@param userIdList { number }?
|
|
224
239
|
]=]
|
|
225
|
-
function DataStore
|
|
240
|
+
function DataStore.SetUserIdList(self: DataStore, userIdList: { number }?)
|
|
226
241
|
assert(type(userIdList) == "table" or userIdList == nil, "Bad userIdList")
|
|
227
242
|
assert(not Symbol.isSymbol(userIdList), "Should not be symbol")
|
|
228
243
|
|
|
@@ -234,7 +249,7 @@ end
|
|
|
234
249
|
|
|
235
250
|
@return { number }?
|
|
236
251
|
]=]
|
|
237
|
-
function DataStore
|
|
252
|
+
function DataStore.GetUserIdList(self: DataStore): { number }?
|
|
238
253
|
return self._userIdList
|
|
239
254
|
end
|
|
240
255
|
|
|
@@ -243,21 +258,22 @@ end
|
|
|
243
258
|
|
|
244
259
|
@return Promise
|
|
245
260
|
]=]
|
|
246
|
-
function DataStore
|
|
261
|
+
function DataStore.PromiseViewUpToDate(self: DataStore): Promise.Promise<()>
|
|
247
262
|
if self._firstLoadPromise then
|
|
248
263
|
return self._firstLoadPromise
|
|
249
264
|
end
|
|
250
265
|
|
|
251
|
-
|
|
266
|
+
local promise = self:_promiseGetAsyncNoCache()
|
|
267
|
+
self._firstLoadPromise = promise
|
|
252
268
|
|
|
253
|
-
|
|
269
|
+
promise:Tap(function()
|
|
254
270
|
self._loadedOk.Value = true
|
|
255
271
|
end)
|
|
256
272
|
|
|
257
|
-
return
|
|
273
|
+
return promise
|
|
258
274
|
end
|
|
259
275
|
|
|
260
|
-
function DataStore
|
|
276
|
+
function DataStore._setupAutoSaving(self: DataStore)
|
|
261
277
|
local startTime = os.clock()
|
|
262
278
|
|
|
263
279
|
self._maid:GiveTask(Rx.combineLatest({
|
|
@@ -307,17 +323,18 @@ function DataStore:_setupAutoSaving()
|
|
|
307
323
|
end))
|
|
308
324
|
end
|
|
309
325
|
|
|
310
|
-
function DataStore
|
|
326
|
+
function DataStore._syncData(self: DataStore, doMergeNewData: boolean)
|
|
311
327
|
if self:DidLoadFail() then
|
|
312
328
|
warn("[DataStore] - Not syncing, failed to load")
|
|
313
329
|
return Promise.rejected("Load not successful, not syncing")
|
|
314
330
|
end
|
|
315
331
|
|
|
316
|
-
return self._maid
|
|
332
|
+
return self._maid
|
|
333
|
+
:GivePromise(self:PromiseViewUpToDate())
|
|
317
334
|
:Then(function()
|
|
318
335
|
return self._maid:GivePromise(self:PromiseInvokeSavingCallbacks())
|
|
319
336
|
end)
|
|
320
|
-
:Then(function()
|
|
337
|
+
:Then(function(): Promise.Promise<()>?
|
|
321
338
|
if not self:HasWritableData() then
|
|
322
339
|
if doMergeNewData then
|
|
323
340
|
-- Reads are cheaper than update async calls
|
|
@@ -336,7 +353,7 @@ function DataStore:_syncData(doMergeNewData: boolean)
|
|
|
336
353
|
end)
|
|
337
354
|
end
|
|
338
355
|
|
|
339
|
-
function DataStore
|
|
356
|
+
function DataStore._doDataSync(self: DataStore, writer, doMergeNewData: boolean): Promise.Promise<()>
|
|
340
357
|
assert(type(doMergeNewData) == "boolean", "Bad doMergeNewData")
|
|
341
358
|
|
|
342
359
|
-- Cache user id list
|
|
@@ -352,63 +369,75 @@ function DataStore:_doDataSync(writer, doMergeNewData)
|
|
|
352
369
|
end
|
|
353
370
|
|
|
354
371
|
-- This is, of course, dangerous, because we won't merge
|
|
355
|
-
promise:Resolve(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
372
|
+
promise:Resolve(
|
|
373
|
+
maid:GivePromise(DataStorePromises.removeAsync(self._robloxDataStore, self._key)):Then(function(): any
|
|
374
|
+
if doMergeNewData then
|
|
375
|
+
-- Write our data
|
|
376
|
+
self:MarkDataAsSaved(writer)
|
|
359
377
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
378
|
+
-- Do syncing after
|
|
379
|
+
return self:_promiseGetAsyncNoCache()
|
|
380
|
+
end
|
|
363
381
|
|
|
364
|
-
|
|
365
|
-
|
|
382
|
+
return nil
|
|
383
|
+
end)
|
|
384
|
+
)
|
|
366
385
|
else
|
|
367
386
|
if self._debugWriting then
|
|
368
|
-
print(
|
|
387
|
+
print(
|
|
388
|
+
string.format(
|
|
389
|
+
"[DataStore] - DataStorePromises.updateAsync(%q) with doMergeNewData = %s",
|
|
390
|
+
self._key,
|
|
391
|
+
tostring(doMergeNewData)
|
|
392
|
+
)
|
|
393
|
+
)
|
|
369
394
|
end
|
|
370
395
|
|
|
371
|
-
promise:Resolve(
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
396
|
+
promise:Resolve(
|
|
397
|
+
maid:GivePromise(
|
|
398
|
+
DataStorePromises.updateAsync(self._robloxDataStore, self._key, function(original, datastoreKeyInfo)
|
|
399
|
+
if promise:IsRejected() then
|
|
400
|
+
-- Cancel if we have another request
|
|
401
|
+
return nil
|
|
402
|
+
end
|
|
376
403
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
404
|
+
local diffSnapshot
|
|
405
|
+
if doMergeNewData then
|
|
406
|
+
diffSnapshot = writer:ComputeDiffSnapshot(original)
|
|
407
|
+
end
|
|
381
408
|
|
|
382
|
-
|
|
409
|
+
local result = writer:WriteMerge(original)
|
|
383
410
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
411
|
+
if result == DataStoreDeleteToken or result == nil then
|
|
412
|
+
result = {}
|
|
413
|
+
end
|
|
387
414
|
|
|
388
|
-
|
|
415
|
+
self:_checkSnapshotIntegrity("writer:WriteMerge(original)", result)
|
|
389
416
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
417
|
+
if self._debugWriting then
|
|
418
|
+
print("[DataStore] - Writing", result)
|
|
419
|
+
end
|
|
393
420
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
421
|
+
if doMergeNewData then
|
|
422
|
+
-- This prevents resaving at high frequency
|
|
423
|
+
self:MarkDataAsSaved(writer)
|
|
424
|
+
self:MergeDiffSnapshot(diffSnapshot)
|
|
425
|
+
end
|
|
399
426
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
427
|
+
local userIdList = writer:GetUserIdList()
|
|
428
|
+
if datastoreKeyInfo then
|
|
429
|
+
userIdList = datastoreKeyInfo:GetUserIds()
|
|
430
|
+
end
|
|
404
431
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
432
|
+
local metadata = nil
|
|
433
|
+
if datastoreKeyInfo then
|
|
434
|
+
metadata = datastoreKeyInfo:GetMetadata()
|
|
435
|
+
end
|
|
409
436
|
|
|
410
|
-
|
|
411
|
-
|
|
437
|
+
return result, userIdList, metadata
|
|
438
|
+
end)
|
|
439
|
+
)
|
|
440
|
+
)
|
|
412
441
|
end
|
|
413
442
|
|
|
414
443
|
promise:Tap(nil, function(err)
|
|
@@ -425,7 +454,7 @@ function DataStore:_doDataSync(writer, doMergeNewData)
|
|
|
425
454
|
return promise
|
|
426
455
|
end
|
|
427
456
|
|
|
428
|
-
function DataStore
|
|
457
|
+
function DataStore._promiseGetAsyncNoCache(self: DataStore): Promise.Promise<()>
|
|
429
458
|
return self._maid:GivePromise(DataStorePromises.getAsync(self._robloxDataStore, self._key))
|
|
430
459
|
:Catch(function(err)
|
|
431
460
|
warn(string.format("DataStorePromises.getAsync(%q) -> warning - %s", tostring(self._key), tostring(err or "empty error")))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Provides a data storage facility with an ability to get sub-stores. So you can write
|
|
3
4
|
directly to this store, overwriting all children, or you can have more partial control
|
|
@@ -36,13 +37,27 @@ local DataStoreStage = setmetatable({}, BaseObject)
|
|
|
36
37
|
DataStoreStage.ClassName = "DataStoreStage"
|
|
37
38
|
DataStoreStage.__index = DataStoreStage
|
|
38
39
|
|
|
40
|
+
export type DataStoreStageKey = string | number
|
|
41
|
+
|
|
42
|
+
export type DataStoreCallback = () -> Promise.Promise<()>?
|
|
43
|
+
|
|
39
44
|
export type DataStoreStage = typeof(setmetatable(
|
|
40
45
|
{} :: {
|
|
46
|
+
_loadName: DataStoreStageKey,
|
|
47
|
+
_loadParent: DataStoreStage?,
|
|
48
|
+
_saveDataSnapshot: any,
|
|
49
|
+
_fullPath: string?,
|
|
50
|
+
_baseDataSnapshot: any,
|
|
51
|
+
_viewSnapshot: any,
|
|
52
|
+
_stores: { [DataStoreStageKey]: DataStoreStage },
|
|
53
|
+
_savingCallbacks: { DataStoreCallback },
|
|
54
|
+
_keySubscriptions: ObservableSubscriptionTable.ObservableSubscriptionTable<any>,
|
|
55
|
+
|
|
41
56
|
Changed: Signal.Signal<any>,
|
|
42
57
|
DataStored: Signal.Signal<any>,
|
|
43
58
|
},
|
|
44
|
-
DataStoreStage
|
|
45
|
-
))
|
|
59
|
+
{} :: typeof({ __index = DataStoreStage })
|
|
60
|
+
)) & BaseObject.BaseObject
|
|
46
61
|
|
|
47
62
|
--[=[
|
|
48
63
|
Constructs a new DataStoreStage to load from. Prefer to use DataStore because this doesn't
|
|
@@ -59,8 +74,8 @@ export type DataStoreStage = typeof(setmetatable(
|
|
|
59
74
|
@param loadParent DataStoreStage?
|
|
60
75
|
@return DataStoreStage
|
|
61
76
|
]=]
|
|
62
|
-
function DataStoreStage.new(loadName:
|
|
63
|
-
local self = setmetatable(BaseObject.new(), DataStoreStage)
|
|
77
|
+
function DataStoreStage.new(loadName: DataStoreStageKey, loadParent: DataStoreStage?): DataStoreStage
|
|
78
|
+
local self: DataStoreStage = setmetatable(BaseObject.new() :: any, DataStoreStage)
|
|
64
79
|
|
|
65
80
|
-- LoadParent is optional, used for loading
|
|
66
81
|
self._loadName = loadName
|
|
@@ -94,7 +109,7 @@ end
|
|
|
94
109
|
@param key string
|
|
95
110
|
@param value any
|
|
96
111
|
]=]
|
|
97
|
-
function DataStoreStage
|
|
112
|
+
function DataStoreStage.Store(self: DataStoreStage, key: string, value: any)
|
|
98
113
|
assert(type(key) == "string", "Bad key")
|
|
99
114
|
|
|
100
115
|
if value == nil then
|
|
@@ -120,7 +135,7 @@ end
|
|
|
120
135
|
@param defaultValue T?
|
|
121
136
|
@return Promise<T>
|
|
122
137
|
]=]
|
|
123
|
-
function DataStoreStage
|
|
138
|
+
function DataStoreStage.Load<T>(self: DataStoreStage, key: DataStoreStageKey, defaultValue: T?): Promise.Promise<T>
|
|
124
139
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
125
140
|
|
|
126
141
|
return self:PromiseViewUpToDate():Then(function()
|
|
@@ -149,7 +164,7 @@ end
|
|
|
149
164
|
@param defaultValue any
|
|
150
165
|
@return Promise<any>
|
|
151
166
|
]=]
|
|
152
|
-
function DataStoreStage
|
|
167
|
+
function DataStoreStage.LoadAll(self: DataStoreStage, defaultValue)
|
|
153
168
|
return self:PromiseViewUpToDate():Then(function()
|
|
154
169
|
if self._viewSnapshot == nil then
|
|
155
170
|
return defaultValue
|
|
@@ -173,7 +188,7 @@ end
|
|
|
173
188
|
@param key string | number
|
|
174
189
|
@return DataStoreStage
|
|
175
190
|
]=]
|
|
176
|
-
function DataStoreStage
|
|
191
|
+
function DataStoreStage.GetSubStore(self: DataStoreStage, key: DataStoreStageKey)
|
|
177
192
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
178
193
|
|
|
179
194
|
if self._stores[key] then
|
|
@@ -228,7 +243,7 @@ end
|
|
|
228
243
|
|
|
229
244
|
@param key string | number
|
|
230
245
|
]=]
|
|
231
|
-
function DataStoreStage
|
|
246
|
+
function DataStoreStage.Delete(self: DataStoreStage, key: string)
|
|
232
247
|
assert(type(key) == "string", "Bad key")
|
|
233
248
|
|
|
234
249
|
self:_storeAtKey(key, DataStoreDeleteToken)
|
|
@@ -237,7 +252,7 @@ end
|
|
|
237
252
|
--[=[
|
|
238
253
|
Queues up a wipe of all values. This will completely set the data to nil.
|
|
239
254
|
]=]
|
|
240
|
-
function DataStoreStage
|
|
255
|
+
function DataStoreStage.Wipe(self: DataStoreStage)
|
|
241
256
|
self:Overwrite(DataStoreDeleteToken)
|
|
242
257
|
end
|
|
243
258
|
|
|
@@ -250,7 +265,7 @@ end
|
|
|
250
265
|
@param defaultValue T?
|
|
251
266
|
@return Observable<T>
|
|
252
267
|
]=]
|
|
253
|
-
function DataStoreStage
|
|
268
|
+
function DataStoreStage.Observe(self: DataStoreStage, key, defaultValue)
|
|
254
269
|
assert(type(key) == "string" or type(key) == "number" or key == nil, "Bad key")
|
|
255
270
|
|
|
256
271
|
if key == nil then
|
|
@@ -282,16 +297,13 @@ function DataStoreStage:Observe(key, defaultValue)
|
|
|
282
297
|
return Observable.new(function(sub)
|
|
283
298
|
local maid = Maid.new()
|
|
284
299
|
|
|
285
|
-
maid:GiveTask(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
end),
|
|
293
|
-
sub:GetFailComplete()
|
|
294
|
-
)
|
|
300
|
+
maid:GiveTask(self._keySubscriptions:Observe(key):Subscribe(function(value)
|
|
301
|
+
if value == nil then
|
|
302
|
+
sub:Fire(defaultValue)
|
|
303
|
+
else
|
|
304
|
+
sub:Fire(value)
|
|
305
|
+
end
|
|
306
|
+
end, sub:GetFailComplete()))
|
|
295
307
|
|
|
296
308
|
-- Load initially
|
|
297
309
|
maid:GivePromise(self:Load(key, defaultValue)):Then(function(value)
|
|
@@ -310,7 +322,7 @@ end
|
|
|
310
322
|
@param callback function -- May return a promise
|
|
311
323
|
@return function -- Call to remove
|
|
312
324
|
]=]
|
|
313
|
-
function DataStoreStage
|
|
325
|
+
function DataStoreStage.AddSavingCallback(self: DataStoreStage, callback: DataStoreCallback?)
|
|
314
326
|
assert(type(callback) == "function", "Bad callback")
|
|
315
327
|
|
|
316
328
|
table.insert(self._savingCallbacks, callback)
|
|
@@ -326,7 +338,7 @@ end
|
|
|
326
338
|
Removes a saving callback from the data store stage
|
|
327
339
|
@param callback function
|
|
328
340
|
]=]
|
|
329
|
-
function DataStoreStage
|
|
341
|
+
function DataStoreStage.RemoveSavingCallback(self: DataStoreStage, callback: DataStoreCallback?)
|
|
330
342
|
assert(type(callback) == "function", "Bad callback")
|
|
331
343
|
|
|
332
344
|
local index = table.find(self._savingCallbacks, callback)
|
|
@@ -340,7 +352,7 @@ end
|
|
|
340
352
|
|
|
341
353
|
@return Signal
|
|
342
354
|
]=]
|
|
343
|
-
function DataStoreStage
|
|
355
|
+
function DataStoreStage.GetTopLevelDataStoredSignal(self: DataStoreStage)
|
|
344
356
|
return self.DataStored
|
|
345
357
|
end
|
|
346
358
|
|
|
@@ -349,15 +361,17 @@ end
|
|
|
349
361
|
|
|
350
362
|
@return string
|
|
351
363
|
]=]
|
|
352
|
-
function DataStoreStage
|
|
364
|
+
function DataStoreStage.GetFullPath(self: DataStoreStage): string
|
|
353
365
|
if self._fullPath then
|
|
354
366
|
return self._fullPath
|
|
355
367
|
elseif self._loadParent then
|
|
356
|
-
|
|
357
|
-
|
|
368
|
+
local fullPath = self._loadParent:GetFullPath() .. "." .. tostring(self._loadName)
|
|
369
|
+
self._fullPath = fullPath
|
|
370
|
+
return fullPath
|
|
358
371
|
else
|
|
359
|
-
|
|
360
|
-
|
|
372
|
+
local fullPath = tostring(self._loadName)
|
|
373
|
+
self._fullPath = fullPath
|
|
374
|
+
return fullPath
|
|
361
375
|
end
|
|
362
376
|
end
|
|
363
377
|
|
|
@@ -366,15 +380,14 @@ end
|
|
|
366
380
|
|
|
367
381
|
@return Promise<{ string }>
|
|
368
382
|
]=]
|
|
369
|
-
function DataStoreStage
|
|
370
|
-
return self:PromiseKeySet()
|
|
371
|
-
|
|
372
|
-
local list = {}
|
|
383
|
+
function DataStoreStage.PromiseKeyList(self: DataStoreStage): Promise.Promise<{ string }>
|
|
384
|
+
return self:PromiseKeySet():Then(function(keys)
|
|
385
|
+
local list = {}
|
|
373
386
|
for key, _ in keys do
|
|
374
387
|
table.insert(list, key)
|
|
375
388
|
end
|
|
376
389
|
return list
|
|
377
|
-
|
|
390
|
+
end)
|
|
378
391
|
end
|
|
379
392
|
|
|
380
393
|
--[=[
|
|
@@ -382,7 +395,7 @@ end
|
|
|
382
395
|
|
|
383
396
|
@return Promise<{ [string]: true }>
|
|
384
397
|
]=]
|
|
385
|
-
function DataStoreStage
|
|
398
|
+
function DataStoreStage.PromiseKeySet(self: DataStoreStage): Promise.Promise<{ [string]: true }>
|
|
386
399
|
return self:PromiseViewUpToDate():Then(function()
|
|
387
400
|
if type(self._viewSnapshot) == "table" then
|
|
388
401
|
return Set.fromKeys(self._viewSnapshot)
|
|
@@ -402,7 +415,7 @@ end
|
|
|
402
415
|
|
|
403
416
|
@param diffSnapshot any
|
|
404
417
|
]=]
|
|
405
|
-
function DataStoreStage
|
|
418
|
+
function DataStoreStage.MergeDiffSnapshot(self: DataStoreStage, diffSnapshot)
|
|
406
419
|
self:_checkIntegrity()
|
|
407
420
|
|
|
408
421
|
self._baseDataSnapshot = self:_updateStoresAndComputeBaseDataSnapshotFromDiffSnapshot(diffSnapshot)
|
|
@@ -419,7 +432,7 @@ end
|
|
|
419
432
|
|
|
420
433
|
@param parentWriter DataStoreWriter
|
|
421
434
|
]=]
|
|
422
|
-
function DataStoreStage
|
|
435
|
+
function DataStoreStage.MarkDataAsSaved(self: DataStoreStage, parentWriter: DataStoreWriter.DataStoreWriter)
|
|
423
436
|
-- Update all children first
|
|
424
437
|
for key, subwriter in pairs(parentWriter:GetSubWritersMap()) do
|
|
425
438
|
local store = self._stores[key]
|
|
@@ -489,7 +502,7 @@ end
|
|
|
489
502
|
|
|
490
503
|
@return Promise
|
|
491
504
|
]=]
|
|
492
|
-
function DataStoreStage
|
|
505
|
+
function DataStoreStage.PromiseViewUpToDate(self: DataStoreStage)
|
|
493
506
|
if not self._loadParent then
|
|
494
507
|
error("[DataStoreStage.Load] - Failed to load, no loadParent!")
|
|
495
508
|
end
|
|
@@ -510,7 +523,7 @@ end
|
|
|
510
523
|
|
|
511
524
|
@param data any
|
|
512
525
|
]=]
|
|
513
|
-
function DataStoreStage
|
|
526
|
+
function DataStoreStage.Overwrite(self: DataStoreStage, data)
|
|
514
527
|
-- Ensure that we at least start loading (and thus the autosave loop) for write
|
|
515
528
|
self:PromiseViewUpToDate()
|
|
516
529
|
|
|
@@ -522,7 +535,7 @@ function DataStoreStage:Overwrite(data)
|
|
|
522
535
|
local newSaveSnapshot = {}
|
|
523
536
|
|
|
524
537
|
local remaining = Set.fromKeys(self._stores)
|
|
525
|
-
for key, store in self._stores do
|
|
538
|
+
for key, store: any in self._stores do
|
|
526
539
|
-- Update each store
|
|
527
540
|
store:Overwrite(data[key])
|
|
528
541
|
end
|
|
@@ -542,7 +555,7 @@ function DataStoreStage:Overwrite(data)
|
|
|
542
555
|
|
|
543
556
|
self._saveDataSnapshot = table.freeze(newSaveSnapshot)
|
|
544
557
|
else
|
|
545
|
-
for _, store in self._stores do
|
|
558
|
+
for _, store: any in self._stores do
|
|
546
559
|
store:Overwrite(DataStoreDeleteToken)
|
|
547
560
|
end
|
|
548
561
|
|
|
@@ -563,7 +576,7 @@ end
|
|
|
563
576
|
|
|
564
577
|
@param data any
|
|
565
578
|
]=]
|
|
566
|
-
function DataStoreStage
|
|
579
|
+
function DataStoreStage.OverwriteMerge(self: DataStoreStage, data)
|
|
567
580
|
-- Ensure that we at least start loading (and thus the autosave loop) for write
|
|
568
581
|
self:PromiseViewUpToDate()
|
|
569
582
|
|
|
@@ -585,7 +598,7 @@ end
|
|
|
585
598
|
@param valueObj Instance -- ValueBase object to store on
|
|
586
599
|
@return MaidTask -- Cleanup to remove this writer and free the key.
|
|
587
600
|
]=]
|
|
588
|
-
function DataStoreStage
|
|
601
|
+
function DataStoreStage.StoreOnValueChange(self: DataStoreStage, name: DataStoreStageKey, valueObj)
|
|
589
602
|
assert(type(name) == "string" or type(name) == "number", "Bad name")
|
|
590
603
|
assert(typeof(valueObj) == "Instance" or (type(valueObj) == "table" and valueObj.Changed), "Bad valueObj")
|
|
591
604
|
|
|
@@ -610,14 +623,14 @@ end
|
|
|
610
623
|
|
|
611
624
|
@return boolean
|
|
612
625
|
]=]
|
|
613
|
-
function DataStoreStage
|
|
626
|
+
function DataStoreStage.HasWritableData(self: DataStoreStage): boolean
|
|
614
627
|
if self._saveDataSnapshot ~= nil then
|
|
615
628
|
return true
|
|
616
629
|
end
|
|
617
630
|
|
|
618
|
-
for name, store in self._stores do
|
|
631
|
+
for name, store: any in self._stores do
|
|
619
632
|
if not store.Destroy then
|
|
620
|
-
warn(string.format("[DataStoreStage] - Substore %q destroyed", name))
|
|
633
|
+
warn(string.format("[DataStoreStage] - Substore %q destroyed", tostring(name)))
|
|
621
634
|
continue
|
|
622
635
|
end
|
|
623
636
|
|
|
@@ -638,7 +651,7 @@ end
|
|
|
638
651
|
|
|
639
652
|
@return DataStoreWriter
|
|
640
653
|
]=]
|
|
641
|
-
function DataStoreStage
|
|
654
|
+
function DataStoreStage.GetNewWriter(self: DataStoreStage): DataStoreWriter.DataStoreWriter
|
|
642
655
|
self:_checkIntegrity()
|
|
643
656
|
|
|
644
657
|
local writer = DataStoreWriter.new(self:GetFullPath())
|
|
@@ -649,9 +662,9 @@ function DataStoreStage:GetNewWriter()
|
|
|
649
662
|
writer:SetSaveDataSnapshot(self._saveDataSnapshot)
|
|
650
663
|
end
|
|
651
664
|
|
|
652
|
-
for key, store in self._stores do
|
|
665
|
+
for key, store: any in self._stores do
|
|
653
666
|
if not store.Destroy then
|
|
654
|
-
warn(string.format("[DataStoreStage] - Substore %q destroyed", key))
|
|
667
|
+
warn(string.format("[DataStoreStage] - Substore %q destroyed", tostring(key)))
|
|
655
668
|
continue
|
|
656
669
|
end
|
|
657
670
|
|
|
@@ -674,17 +687,17 @@ end
|
|
|
674
687
|
|
|
675
688
|
@return Promise
|
|
676
689
|
]=]
|
|
677
|
-
function DataStoreStage
|
|
678
|
-
local removingPromises = {}
|
|
690
|
+
function DataStoreStage.PromiseInvokeSavingCallbacks(self: DataStoreStage)
|
|
691
|
+
local removingPromises: { Promise.Promise<()> } = {}
|
|
679
692
|
|
|
680
693
|
for _, func in self._savingCallbacks do
|
|
681
694
|
local result = func()
|
|
682
695
|
if Promise.isPromise(result) then
|
|
683
|
-
table.insert(removingPromises, result)
|
|
696
|
+
table.insert(removingPromises, result :: any)
|
|
684
697
|
end
|
|
685
698
|
end
|
|
686
699
|
|
|
687
|
-
for _, substore in self._stores do
|
|
700
|
+
for _, substore: any in self._stores do
|
|
688
701
|
local promise = substore:PromiseInvokeSavingCallbacks()
|
|
689
702
|
if promise then
|
|
690
703
|
table.insert(removingPromises, promise)
|
|
@@ -694,7 +707,7 @@ function DataStoreStage:PromiseInvokeSavingCallbacks()
|
|
|
694
707
|
return PromiseUtils.all(removingPromises)
|
|
695
708
|
end
|
|
696
709
|
|
|
697
|
-
function DataStoreStage
|
|
710
|
+
function DataStoreStage._createFullBaseDataSnapshot(self: DataStoreStage)
|
|
698
711
|
if self._baseDataSnapshot == DataStoreDeleteToken then
|
|
699
712
|
error("BadDataSnapshot cannot be a delete token")
|
|
700
713
|
elseif type(self._baseDataSnapshot) == "table" or self._baseDataSnapshot == nil then
|
|
@@ -705,9 +718,9 @@ function DataStoreStage:_createFullBaseDataSnapshot()
|
|
|
705
718
|
newSnapshot = {}
|
|
706
719
|
end
|
|
707
720
|
|
|
708
|
-
for key, store in self._stores do
|
|
721
|
+
for key, store: any in self._stores do
|
|
709
722
|
if not store.Destroy then
|
|
710
|
-
warn(string.format("[DataStoreStage] - Substore %q destroyed", key))
|
|
723
|
+
warn(string.format("[DataStoreStage] - Substore %q destroyed", tostring(key)))
|
|
711
724
|
continue
|
|
712
725
|
end
|
|
713
726
|
|
|
@@ -726,7 +739,7 @@ function DataStoreStage:_createFullBaseDataSnapshot()
|
|
|
726
739
|
end
|
|
727
740
|
end
|
|
728
741
|
|
|
729
|
-
function DataStoreStage
|
|
742
|
+
function DataStoreStage._updateStoresAndComputeBaseDataSnapshotFromDiffSnapshot(self: DataStoreStage, diffSnapshot)
|
|
730
743
|
if diffSnapshot == DataStoreDeleteToken then
|
|
731
744
|
return nil
|
|
732
745
|
elseif type(diffSnapshot) == "table" then
|
|
@@ -752,7 +765,11 @@ function DataStoreStage:_updateStoresAndComputeBaseDataSnapshotFromDiffSnapshot(
|
|
|
752
765
|
end
|
|
753
766
|
end
|
|
754
767
|
|
|
755
|
-
function DataStoreStage
|
|
768
|
+
function DataStoreStage._updateStoresAndComputeBaseDataSnapshotValueFromDiffSnapshot(
|
|
769
|
+
self: DataStoreStage,
|
|
770
|
+
key: DataStoreStageKey,
|
|
771
|
+
value
|
|
772
|
+
)
|
|
756
773
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
757
774
|
|
|
758
775
|
if self._stores[key] then
|
|
@@ -771,7 +788,7 @@ function DataStoreStage:_updateStoresAndComputeBaseDataSnapshotValueFromDiffSnap
|
|
|
771
788
|
end
|
|
772
789
|
end
|
|
773
790
|
|
|
774
|
-
function DataStoreStage
|
|
791
|
+
function DataStoreStage._recurseMergeTable(self: DataStoreStage, original, incoming)
|
|
775
792
|
if incoming == DataStoreDeleteToken then
|
|
776
793
|
return nil
|
|
777
794
|
elseif type(incoming) == "table" and type(original) == "table" then
|
|
@@ -789,7 +806,7 @@ function DataStoreStage:_recurseMergeTable(original, incoming)
|
|
|
789
806
|
end
|
|
790
807
|
end
|
|
791
808
|
|
|
792
|
-
function DataStoreStage
|
|
809
|
+
function DataStoreStage._updateViewSnapshot(self: DataStoreStage)
|
|
793
810
|
self:_checkIntegrity()
|
|
794
811
|
|
|
795
812
|
local newViewSnapshot = self:_computeNewViewSnapshot()
|
|
@@ -818,7 +835,7 @@ function DataStoreStage:_updateViewSnapshot()
|
|
|
818
835
|
self:_checkIntegrity()
|
|
819
836
|
end
|
|
820
837
|
|
|
821
|
-
function DataStoreStage
|
|
838
|
+
function DataStoreStage._computeChangedKeys(_self: DataStoreStage, previousViewSnapshot, newViewSnapshot)
|
|
822
839
|
-- Detect keys that changed
|
|
823
840
|
if type(previousViewSnapshot) == "table" and type(newViewSnapshot) == "table" then
|
|
824
841
|
local changedKeys = {}
|
|
@@ -842,7 +859,7 @@ function DataStoreStage:_computeChangedKeys(previousViewSnapshot, newViewSnapsho
|
|
|
842
859
|
end
|
|
843
860
|
end
|
|
844
861
|
|
|
845
|
-
function DataStoreStage
|
|
862
|
+
function DataStoreStage._updateViewSnapshotAtKey(self: DataStoreStage, key)
|
|
846
863
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
847
864
|
|
|
848
865
|
if type(self._viewSnapshot) ~= "table" then
|
|
@@ -866,7 +883,7 @@ function DataStoreStage:_updateViewSnapshotAtKey(key)
|
|
|
866
883
|
self:_checkIntegrity()
|
|
867
884
|
end
|
|
868
885
|
|
|
869
|
-
function DataStoreStage
|
|
886
|
+
function DataStoreStage._computeNewViewSnapshot(self: DataStoreStage)
|
|
870
887
|
-- This prioritizes save data first, then stores, then base data
|
|
871
888
|
|
|
872
889
|
if self._saveDataSnapshot == DataStoreDeleteToken then
|
|
@@ -916,7 +933,7 @@ function DataStoreStage:_computeNewViewSnapshot()
|
|
|
916
933
|
end
|
|
917
934
|
end
|
|
918
935
|
|
|
919
|
-
function DataStoreStage
|
|
936
|
+
function DataStoreStage._computeViewValueForKey(self: DataStoreStage, key)
|
|
920
937
|
-- This prioritizes save data first, then stores, then base data
|
|
921
938
|
|
|
922
939
|
if self._saveDataSnapshot == DataStoreDeleteToken then
|
|
@@ -956,7 +973,7 @@ function DataStoreStage:_computeViewValueForKey(key)
|
|
|
956
973
|
end
|
|
957
974
|
|
|
958
975
|
-- Stores the data for overwrite.
|
|
959
|
-
function DataStoreStage
|
|
976
|
+
function DataStoreStage._storeAtKey(self: DataStoreStage, key, value)
|
|
960
977
|
assert(type(key) == "string" or type(key) == "number", "Bad key")
|
|
961
978
|
assert(value ~= nil, "Bad value")
|
|
962
979
|
|
|
@@ -996,7 +1013,7 @@ function DataStoreStage:_storeAtKey(key, value)
|
|
|
996
1013
|
self:_checkIntegrity()
|
|
997
1014
|
end
|
|
998
1015
|
|
|
999
|
-
function DataStoreStage
|
|
1016
|
+
function DataStoreStage._checkSnapshotIntegrity(_self: DataStoreStage, label, result)
|
|
1000
1017
|
assert(type(label) == "string", "Bad label")
|
|
1001
1018
|
|
|
1002
1019
|
if not SLOW_INTEGRITY_CHECK_ENABLED then
|
|
@@ -1007,7 +1024,7 @@ function DataStoreStage:_checkSnapshotIntegrity(label, result)
|
|
|
1007
1024
|
error(string.format("%s should not be a DataStoreDeleteToken", label))
|
|
1008
1025
|
end
|
|
1009
1026
|
|
|
1010
|
-
local function recurse(innerLabel, value)
|
|
1027
|
+
local function recurse(innerLabel: string, value)
|
|
1011
1028
|
if type(value) ~= "table" then
|
|
1012
1029
|
return
|
|
1013
1030
|
end
|
|
@@ -1032,7 +1049,7 @@ function DataStoreStage:_checkSnapshotIntegrity(label, result)
|
|
|
1032
1049
|
recurse(label, result)
|
|
1033
1050
|
end
|
|
1034
1051
|
|
|
1035
|
-
function DataStoreStage
|
|
1052
|
+
function DataStoreStage._checkIntegrity(self: DataStoreStage)
|
|
1036
1053
|
if not SLOW_INTEGRITY_CHECK_ENABLED then
|
|
1037
1054
|
return
|
|
1038
1055
|
end
|
|
@@ -1056,11 +1073,11 @@ function DataStoreStage:_checkIntegrity()
|
|
|
1056
1073
|
|
|
1057
1074
|
for key, _ in self._stores do
|
|
1058
1075
|
if type(self._baseDataSnapshot) == "table" and self._baseDataSnapshot[key] ~= nil then
|
|
1059
|
-
error(string.format("[DataStoreStage] - Duplicate baseData at key %q", key))
|
|
1076
|
+
error(string.format("[DataStoreStage] - Duplicate baseData at key %q", tostring(key)))
|
|
1060
1077
|
end
|
|
1061
1078
|
|
|
1062
1079
|
if type(self._saveDataSnapshot) == "table" and self._saveDataSnapshot[key] ~= nil then
|
|
1063
|
-
error(string.format("[DataStoreStage] - Duplicate saveData at key %q", key))
|
|
1080
|
+
error(string.format("[DataStoreStage] - Duplicate saveData at key %q", tostring(key)))
|
|
1064
1081
|
end
|
|
1065
1082
|
end
|
|
1066
1083
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Captures a snapshot of data to write and then merges it with the original.
|
|
3
4
|
@server
|
|
@@ -18,14 +19,27 @@ local DataStoreWriter = {}
|
|
|
18
19
|
DataStoreWriter.ClassName = "DataStoreWriter"
|
|
19
20
|
DataStoreWriter.__index = DataStoreWriter
|
|
20
21
|
|
|
22
|
+
export type DataStoreStageKey = string | number
|
|
23
|
+
|
|
24
|
+
export type DataStoreWriter = typeof(setmetatable(
|
|
25
|
+
{} :: {
|
|
26
|
+
_debugName: string,
|
|
27
|
+
_saveDataSnapshot: any,
|
|
28
|
+
_fullBaseDataSnapshot: any,
|
|
29
|
+
_userIdList: { number } | nil,
|
|
30
|
+
_writers: { [DataStoreStageKey]: DataStoreWriter },
|
|
31
|
+
},
|
|
32
|
+
{} :: typeof({ __index = DataStoreWriter })
|
|
33
|
+
))
|
|
34
|
+
|
|
21
35
|
--[=[
|
|
22
36
|
Constructs a new DataStoreWriter. In general, you will not use this API directly.
|
|
23
37
|
|
|
24
38
|
@param debugName string
|
|
25
39
|
@return DataStoreWriter
|
|
26
40
|
]=]
|
|
27
|
-
function DataStoreWriter.new(debugName: string)
|
|
28
|
-
local self = setmetatable({}, DataStoreWriter)
|
|
41
|
+
function DataStoreWriter.new(debugName: string): DataStoreWriter
|
|
42
|
+
local self: DataStoreWriter = setmetatable({} :: any, DataStoreWriter)
|
|
29
43
|
|
|
30
44
|
self._debugName = assert(debugName, "No debugName")
|
|
31
45
|
self._saveDataSnapshot = UNSET_TOKEN
|
|
@@ -41,7 +55,7 @@ end
|
|
|
41
55
|
Sets the ray data to write
|
|
42
56
|
@param saveDataSnapshot table | any
|
|
43
57
|
]=]
|
|
44
|
-
function DataStoreWriter
|
|
58
|
+
function DataStoreWriter.SetSaveDataSnapshot(self: DataStoreWriter, saveDataSnapshot)
|
|
45
59
|
assert(type(saveDataSnapshot) ~= "table" or table.isfrozen(saveDataSnapshot), "saveDataSnapshot should be frozen")
|
|
46
60
|
|
|
47
61
|
if saveDataSnapshot == DataStoreDeleteToken then
|
|
@@ -53,7 +67,7 @@ function DataStoreWriter:SetSaveDataSnapshot(saveDataSnapshot)
|
|
|
53
67
|
end
|
|
54
68
|
end
|
|
55
69
|
|
|
56
|
-
function DataStoreWriter
|
|
70
|
+
function DataStoreWriter.GetDataToSave(self: DataStoreWriter): any
|
|
57
71
|
if self._saveDataSnapshot == UNSET_TOKEN then
|
|
58
72
|
return nil
|
|
59
73
|
end
|
|
@@ -61,11 +75,11 @@ function DataStoreWriter:GetDataToSave()
|
|
|
61
75
|
return self._saveDataSnapshot
|
|
62
76
|
end
|
|
63
77
|
|
|
64
|
-
function DataStoreWriter
|
|
78
|
+
function DataStoreWriter.GetSubWritersMap(self: DataStoreWriter): { [DataStoreStageKey]: DataStoreWriter }
|
|
65
79
|
return self._writers
|
|
66
80
|
end
|
|
67
81
|
|
|
68
|
-
function DataStoreWriter
|
|
82
|
+
function DataStoreWriter.SetFullBaseDataSnapshot(self: DataStoreWriter, fullBaseDataSnapshot)
|
|
69
83
|
assert(
|
|
70
84
|
type(fullBaseDataSnapshot) ~= "table" or table.isfrozen(fullBaseDataSnapshot),
|
|
71
85
|
"fullBaseDataSnapshot should be frozen"
|
|
@@ -81,11 +95,11 @@ end
|
|
|
81
95
|
|
|
82
96
|
--[=[
|
|
83
97
|
Adds a recursive child writer to use at the key `name`
|
|
84
|
-
@param name string
|
|
98
|
+
@param name string | number
|
|
85
99
|
@param writer DataStoreWriter
|
|
86
100
|
]=]
|
|
87
|
-
function DataStoreWriter
|
|
88
|
-
assert(type(name) == "string", "Bad name")
|
|
101
|
+
function DataStoreWriter.AddSubWriter(self: DataStoreWriter, name: DataStoreStageKey, writer: DataStoreWriter)
|
|
102
|
+
assert(type(name) == "string" or type(name) == "number", "Bad name")
|
|
89
103
|
assert(not self._writers[name], "Writer already exists for name")
|
|
90
104
|
assert(writer, "Bad writer")
|
|
91
105
|
|
|
@@ -95,10 +109,10 @@ end
|
|
|
95
109
|
--[=[
|
|
96
110
|
Gets a sub writer
|
|
97
111
|
|
|
98
|
-
@param name string
|
|
112
|
+
@param name string | number
|
|
99
113
|
@return DataStoreWriter
|
|
100
114
|
]=]
|
|
101
|
-
function DataStoreWriter
|
|
115
|
+
function DataStoreWriter.GetWriter(self: DataStoreWriter, name: DataStoreStageKey): DataStoreWriter?
|
|
102
116
|
assert(type(name) == "string", "Bad name")
|
|
103
117
|
|
|
104
118
|
return self._writers[name]
|
|
@@ -111,7 +125,7 @@ end
|
|
|
111
125
|
|
|
112
126
|
@param incoming any
|
|
113
127
|
]=]
|
|
114
|
-
function DataStoreWriter
|
|
128
|
+
function DataStoreWriter.ComputeDiffSnapshot(self: DataStoreWriter, incoming)
|
|
115
129
|
assert(incoming ~= DataStoreDeleteToken, "Incoming value should not be DataStoreDeleteToken")
|
|
116
130
|
assert(not Symbol.isSymbol(incoming), "Incoming should not be symbol")
|
|
117
131
|
|
|
@@ -149,7 +163,7 @@ function DataStoreWriter:ComputeDiffSnapshot(incoming)
|
|
|
149
163
|
end
|
|
150
164
|
end
|
|
151
165
|
|
|
152
|
-
function DataStoreWriter
|
|
166
|
+
function DataStoreWriter._computeValueDiff(self: DataStoreWriter, original, incoming)
|
|
153
167
|
assert(original ~= DataStoreDeleteToken, "original cannot be DataStoreDeleteToken")
|
|
154
168
|
assert(incoming ~= DataStoreDeleteToken, "incoming cannot be DataStoreDeleteToken")
|
|
155
169
|
assert(not Symbol.isSymbol(original), "original should not be symbol")
|
|
@@ -166,7 +180,7 @@ function DataStoreWriter:_computeValueDiff(original, incoming)
|
|
|
166
180
|
end
|
|
167
181
|
end
|
|
168
182
|
|
|
169
|
-
function DataStoreWriter
|
|
183
|
+
function DataStoreWriter._computeTableDiff(self: DataStoreWriter, original, incoming)
|
|
170
184
|
assert(type(original) == "table", "Bad original")
|
|
171
185
|
assert(type(incoming) == "table", "Bad incoming")
|
|
172
186
|
assert(not Symbol.isSymbol(original), "original should not be symbol")
|
|
@@ -191,7 +205,7 @@ end
|
|
|
191
205
|
|
|
192
206
|
@param userIdList { number }
|
|
193
207
|
]=]
|
|
194
|
-
function DataStoreWriter
|
|
208
|
+
function DataStoreWriter.SetUserIdList(self: DataStoreWriter, userIdList: { number }?)
|
|
195
209
|
assert(type(userIdList) == "table" or userIdList == nil, "Bad userIdList")
|
|
196
210
|
|
|
197
211
|
self._userIdList = userIdList
|
|
@@ -202,7 +216,7 @@ end
|
|
|
202
216
|
|
|
203
217
|
@return userIdList { number }
|
|
204
218
|
]=]
|
|
205
|
-
function DataStoreWriter
|
|
219
|
+
function DataStoreWriter.GetUserIdList(self: DataStoreWriter): { number }?
|
|
206
220
|
if self._userIdList == UNSET_TOKEN then
|
|
207
221
|
return nil
|
|
208
222
|
end
|
|
@@ -210,7 +224,7 @@ function DataStoreWriter:GetUserIdList()
|
|
|
210
224
|
return self._userIdList
|
|
211
225
|
end
|
|
212
226
|
|
|
213
|
-
function DataStoreWriter
|
|
227
|
+
function DataStoreWriter._writeMergeWriters(self: DataStoreWriter, original)
|
|
214
228
|
local copy
|
|
215
229
|
if Symbol.isSymbol(original) then
|
|
216
230
|
copy = original
|
|
@@ -285,7 +299,7 @@ end
|
|
|
285
299
|
@param original any
|
|
286
300
|
@return any -- The original value
|
|
287
301
|
]=]
|
|
288
|
-
function DataStoreWriter
|
|
302
|
+
function DataStoreWriter.WriteMerge(self: DataStoreWriter, original)
|
|
289
303
|
-- Prioritize save value first, followed by writers, followed by original value
|
|
290
304
|
|
|
291
305
|
if self._saveDataSnapshot == DataStoreDeleteToken then
|
|
@@ -302,7 +316,7 @@ function DataStoreWriter:WriteMerge(original)
|
|
|
302
316
|
end
|
|
303
317
|
end
|
|
304
318
|
|
|
305
|
-
function DataStoreWriter
|
|
319
|
+
function DataStoreWriter.IsCompleteWipe(self: DataStoreWriter): boolean
|
|
306
320
|
if self._saveDataSnapshot == UNSET_TOKEN then
|
|
307
321
|
return false
|
|
308
322
|
end
|
|
@@ -64,6 +64,19 @@ local PlayerDataStoreManager = setmetatable({}, BaseObject)
|
|
|
64
64
|
PlayerDataStoreManager.ClassName = "PlayerDataStoreManager"
|
|
65
65
|
PlayerDataStoreManager.__index = PlayerDataStoreManager
|
|
66
66
|
|
|
67
|
+
export type PlayerDataStoreManager = typeof(setmetatable(
|
|
68
|
+
{} :: {
|
|
69
|
+
_robloxDataStore: any,
|
|
70
|
+
_keyGenerator: (Player) -> string,
|
|
71
|
+
_datastores: { [Player]: DataStore },
|
|
72
|
+
_removing: { [Player]: boolean },
|
|
73
|
+
_pendingSaves: PendingPromiseTracker.PendingPromiseTracker<any>,
|
|
74
|
+
_removingCallbacks: { (Player) -> any },
|
|
75
|
+
_disableSavingInStudio: boolean?,
|
|
76
|
+
},
|
|
77
|
+
{} :: typeof({ __index = PlayerDataStoreManager })
|
|
78
|
+
)) & BaseObject.BaseObject
|
|
79
|
+
|
|
67
80
|
--[=[
|
|
68
81
|
Constructs a new PlayerDataStoreManager.
|
|
69
82
|
|
|
@@ -16,6 +16,17 @@ local _ServiceBag = require("ServiceBag")
|
|
|
16
16
|
local PlayerDataStoreService = {}
|
|
17
17
|
PlayerDataStoreService.ServiceName = "PlayerDataStoreService"
|
|
18
18
|
|
|
19
|
+
export type PlayerDataStoreService = typeof(setmetatable(
|
|
20
|
+
{} :: {
|
|
21
|
+
_serviceBag: _ServiceBag.ServiceBag,
|
|
22
|
+
_maid: Maid.Maid,
|
|
23
|
+
_dataStoreName: string,
|
|
24
|
+
_dataStoreScope: string,
|
|
25
|
+
_dataStoreManagerPromise: Promise.Promise<PlayerDataStoreManager.PlayerDataStoreManager>,
|
|
26
|
+
},
|
|
27
|
+
{} :: typeof({ __index = PlayerDataStoreService })
|
|
28
|
+
))
|
|
29
|
+
|
|
19
30
|
--[=[
|
|
20
31
|
Initializes the PlayerDataStoreService. Should be done via [ServiceBag.Init].
|
|
21
32
|
@param serviceBag ServiceBag
|
|
@@ -15,6 +15,8 @@ local Table = require("Table")
|
|
|
15
15
|
|
|
16
16
|
local DataStorePromises = {}
|
|
17
17
|
|
|
18
|
+
export type RobloxDataStore = DataStore
|
|
19
|
+
|
|
18
20
|
--[=[
|
|
19
21
|
Promises a Roblox datastore object with the name and scope. Generally only fails
|
|
20
22
|
when you haven't published the place.
|
|
@@ -95,7 +97,7 @@ end
|
|
|
95
97
|
function DataStorePromises.updateAsync<T>(
|
|
96
98
|
robloxDataStore: DataStore,
|
|
97
99
|
key: string,
|
|
98
|
-
updateFunc: (T) -> T?
|
|
100
|
+
updateFunc: (T, DataStoreKeyInfo) -> T?
|
|
99
101
|
): Promise.Promise<(T, DataStoreKeyInfo)>
|
|
100
102
|
assert(typeof(robloxDataStore) == "Instance", "Bad robloxDataStore")
|
|
101
103
|
assert(type(key) == "string", "Bad key")
|