@quenty/datastore 13.20.3 → 13.20.4-canary.11a5dcf.0
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 +16 -0
- package/package.json +15 -15
- package/src/Server/DataStore.lua +19 -5
- package/src/Server/GameDataStoreService.lua +25 -25
- package/src/Server/Modules/DataStoreDeleteToken.lua +2 -1
- package/src/Server/Modules/DataStoreSnapshotUtils.lua +2 -1
- package/src/Server/Modules/DataStoreStage.lua +2 -2
- package/src/Server/Modules/DataStoreWriter.lua +4 -4
- package/src/Server/PlayerDataStoreManager.lua +28 -16
- package/src/Server/PlayerDataStoreService.lua +12 -10
- package/src/Server/PrivateServerDataStoreService.lua +27 -22
- package/src/Server/Utility/DataStorePromises.lua +10 -4
- package/src/Shared/Utility/DataStoreStringUtils.lua +2 -1
- package/test/scripts/Client/ClientMain.client.lua +1 -1
- package/test/scripts/Server/ServerMain.server.lua +5 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.4-canary.11a5dcf.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/datastore@13.20.3...@quenty/datastore@13.20.4-canary.11a5dcf.0) (2025-05-10)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Additional type checking updates ([05ba29a](https://github.com/Quenty/NevermoreEngine/commit/05ba29a03efc9f3feed74b34f1d9dfb237496214))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Add even more types ([b31717d](https://github.com/Quenty/NevermoreEngine/commit/b31717d8c9f7620c457f5018a2affa760a65334a))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## [13.20.3](https://github.com/Quenty/NevermoreEngine/compare/@quenty/datastore@13.20.2...@quenty/datastore@13.20.3) (2025-04-10)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @quenty/datastore
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/datastore",
|
|
3
|
-
"version": "13.20.
|
|
3
|
+
"version": "13.20.4-canary.11a5dcf.0",
|
|
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": "
|
|
30
|
-
"@quenty/bindtocloseservice": "
|
|
31
|
-
"@quenty/loader": "
|
|
32
|
-
"@quenty/maid": "
|
|
33
|
-
"@quenty/math": "
|
|
34
|
-
"@quenty/pagesutils": "
|
|
35
|
-
"@quenty/promise": "
|
|
36
|
-
"@quenty/rx": "
|
|
37
|
-
"@quenty/servicebag": "
|
|
38
|
-
"@quenty/signal": "
|
|
39
|
-
"@quenty/symbol": "
|
|
40
|
-
"@quenty/table": "
|
|
41
|
-
"@quenty/valueobject": "
|
|
29
|
+
"@quenty/baseobject": "10.8.4-canary.11a5dcf.0",
|
|
30
|
+
"@quenty/bindtocloseservice": "8.17.4-canary.11a5dcf.0",
|
|
31
|
+
"@quenty/loader": "10.8.4-canary.11a5dcf.0",
|
|
32
|
+
"@quenty/maid": "3.4.4-canary.11a5dcf.0",
|
|
33
|
+
"@quenty/math": "2.7.3",
|
|
34
|
+
"@quenty/pagesutils": "5.11.5-canary.11a5dcf.0",
|
|
35
|
+
"@quenty/promise": "10.10.5-canary.11a5dcf.0",
|
|
36
|
+
"@quenty/rx": "13.17.4-canary.11a5dcf.0",
|
|
37
|
+
"@quenty/servicebag": "11.11.5-canary.11a5dcf.0",
|
|
38
|
+
"@quenty/signal": "7.10.4-canary.11a5dcf.0",
|
|
39
|
+
"@quenty/symbol": "3.4.3-canary.11a5dcf.0",
|
|
40
|
+
"@quenty/table": "3.7.5-canary.11a5dcf.0",
|
|
41
|
+
"@quenty/valueobject": "13.17.4-canary.11a5dcf.0"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "11a5dcf7d4c7a0bfbf3337e97d30e8346ea09d3f"
|
|
47
47
|
}
|
package/src/Server/DataStore.lua
CHANGED
|
@@ -113,7 +113,7 @@ export type DataStore = typeof(setmetatable(
|
|
|
113
113
|
@param key string
|
|
114
114
|
@return DataStore
|
|
115
115
|
]=]
|
|
116
|
-
function DataStore.new(robloxDataStore: DataStorePromises.RobloxDataStore, key: string)
|
|
116
|
+
function DataStore.new(robloxDataStore: DataStorePromises.RobloxDataStore, key: string): DataStore
|
|
117
117
|
local self: DataStore = setmetatable(DataStoreStage.new(key) :: any, DataStore)
|
|
118
118
|
|
|
119
119
|
self._key = key or error("No key")
|
|
@@ -455,9 +455,16 @@ function DataStore._doDataSync(self: DataStore, writer, doMergeNewData: boolean)
|
|
|
455
455
|
end
|
|
456
456
|
|
|
457
457
|
function DataStore._promiseGetAsyncNoCache(self: DataStore): Promise.Promise<()>
|
|
458
|
-
return self._maid
|
|
458
|
+
return self._maid
|
|
459
|
+
:GivePromise(DataStorePromises.getAsync(self._robloxDataStore, self._key))
|
|
459
460
|
:Catch(function(err)
|
|
460
|
-
warn(
|
|
461
|
+
warn(
|
|
462
|
+
string.format(
|
|
463
|
+
"DataStorePromises.getAsync(%q) -> warning - %s",
|
|
464
|
+
tostring(self._key),
|
|
465
|
+
tostring(err or "empty error")
|
|
466
|
+
)
|
|
467
|
+
)
|
|
461
468
|
return Promise.rejected(err)
|
|
462
469
|
end)
|
|
463
470
|
:Then(function(data)
|
|
@@ -467,10 +474,17 @@ function DataStore._promiseGetAsyncNoCache(self: DataStore): Promise.Promise<()>
|
|
|
467
474
|
self:MergeDiffSnapshot(diffSnapshot)
|
|
468
475
|
|
|
469
476
|
if self._debugWriting then
|
|
470
|
-
print(
|
|
477
|
+
print(
|
|
478
|
+
string.format("DataStorePromises.getAsync(%q) -> Got ", tostring(self._key)),
|
|
479
|
+
data,
|
|
480
|
+
"with diff snapshot",
|
|
481
|
+
diffSnapshot,
|
|
482
|
+
"to view",
|
|
483
|
+
self._viewSnapshot
|
|
484
|
+
)
|
|
471
485
|
-- print(string.format("DataStorePromises.getAsync(%q) -> Got ", self._key), data)
|
|
472
486
|
end
|
|
473
487
|
end)
|
|
474
488
|
end
|
|
475
489
|
|
|
476
|
-
return DataStore
|
|
490
|
+
return DataStore
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Service which manages central access to datastore. This datastore will refresh pretty frequently and
|
|
3
4
|
can be used for configuration and other components, such as Twitter codes or global settings.
|
|
@@ -12,12 +13,12 @@ local DataStore = require("DataStore")
|
|
|
12
13
|
local DataStorePromises = require("DataStorePromises")
|
|
13
14
|
local Maid = require("Maid")
|
|
14
15
|
local Promise = require("Promise")
|
|
15
|
-
local
|
|
16
|
+
local ServiceBag = require("ServiceBag")
|
|
16
17
|
|
|
17
18
|
local GameDataStoreService = {}
|
|
18
19
|
GameDataStoreService.ServiceName = "GameDataStoreService"
|
|
19
20
|
|
|
20
|
-
function GameDataStoreService:Init(serviceBag:
|
|
21
|
+
function GameDataStoreService:Init(serviceBag: ServiceBag.ServiceBag)
|
|
21
22
|
assert(not self._serviceBag, "Already initialized")
|
|
22
23
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
23
24
|
|
|
@@ -26,44 +27,43 @@ function GameDataStoreService:Init(serviceBag: _ServiceBag.ServiceBag)
|
|
|
26
27
|
self._maid = Maid.new()
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
function GameDataStoreService:PromiseDataStore()
|
|
30
|
+
function GameDataStoreService:PromiseDataStore(): Promise.Promise<DataStore>
|
|
30
31
|
if self._dataStorePromise then
|
|
31
32
|
return self._dataStorePromise
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
self._dataStorePromise = self:_promiseRobloxDataStore()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return dataStore
|
|
51
|
-
end)
|
|
35
|
+
self._dataStorePromise = self:_promiseRobloxDataStore():Then(function(robloxDataStore)
|
|
36
|
+
-- Live sync this stuff pretty frequently
|
|
37
|
+
local dataStore: DataStore.DataStore = DataStore.new(robloxDataStore, self:_getKey())
|
|
38
|
+
dataStore:SetSyncOnSave(true)
|
|
39
|
+
dataStore:SetAutoSaveTimeSeconds(5)
|
|
40
|
+
|
|
41
|
+
self._maid:GiveTask(self._bindToCloseService:RegisterPromiseOnCloseCallback(function()
|
|
42
|
+
return Promise.defer(function(resolve)
|
|
43
|
+
return resolve(dataStore:Save())
|
|
44
|
+
end):Finally(function()
|
|
45
|
+
dataStore:Destroy()
|
|
46
|
+
end)
|
|
47
|
+
end))
|
|
48
|
+
|
|
49
|
+
return dataStore
|
|
50
|
+
end)
|
|
52
51
|
|
|
53
52
|
return self._dataStorePromise
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
function GameDataStoreService:_promiseRobloxDataStore()
|
|
55
|
+
function GameDataStoreService:_promiseRobloxDataStore(): Promise.Promise<any>
|
|
57
56
|
if self._robloxDataStorePromise then
|
|
58
57
|
return self._robloxDataStorePromise
|
|
59
58
|
end
|
|
60
59
|
|
|
61
|
-
self._robloxDataStorePromise =
|
|
60
|
+
self._robloxDataStorePromise =
|
|
61
|
+
self._maid:GivePromise(DataStorePromises.promiseDataStore("GameDataStore", "Version1"))
|
|
62
62
|
|
|
63
63
|
return self._robloxDataStorePromise
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
function GameDataStoreService:_getKey()
|
|
66
|
+
function GameDataStoreService:_getKey(): string
|
|
67
67
|
return "version1"
|
|
68
68
|
end
|
|
69
69
|
|
|
@@ -71,4 +71,4 @@ function GameDataStoreService:Destroy()
|
|
|
71
71
|
self._maid:DoCleaning()
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
return GameDataStoreService
|
|
74
|
+
return GameDataStoreService
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Token to use for deleting.
|
|
3
4
|
@server
|
|
@@ -8,4 +9,4 @@ local require = require(script.Parent.loader).load(script)
|
|
|
8
9
|
|
|
9
10
|
local Symbol = require("Symbol")
|
|
10
11
|
|
|
11
|
-
return Symbol.named("dataStoreDeleteToken")
|
|
12
|
+
return Symbol.named("dataStoreDeleteToken")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
@class DataStoreSnapshotUtils
|
|
3
4
|
]=]
|
|
@@ -12,4 +13,4 @@ function DataStoreSnapshotUtils.isEmptySnapshot(snapshot: any): boolean
|
|
|
12
13
|
return not Symbol.isSymbol(snapshot) and type(snapshot) == "table" and next(snapshot) == nil
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
return DataStoreSnapshotUtils
|
|
16
|
+
return DataStoreSnapshotUtils
|
|
@@ -21,13 +21,13 @@ local BaseObject = require("BaseObject")
|
|
|
21
21
|
local DataStoreDeleteToken = require("DataStoreDeleteToken")
|
|
22
22
|
local DataStoreSnapshotUtils = require("DataStoreSnapshotUtils")
|
|
23
23
|
local DataStoreWriter = require("DataStoreWriter")
|
|
24
|
-
local Signal = require("Signal")
|
|
25
24
|
local Maid = require("Maid")
|
|
26
25
|
local Observable = require("Observable")
|
|
27
26
|
local ObservableSubscriptionTable = require("ObservableSubscriptionTable")
|
|
28
27
|
local Promise = require("Promise")
|
|
29
28
|
local PromiseUtils = require("PromiseUtils")
|
|
30
29
|
local Set = require("Set")
|
|
30
|
+
local Signal = require("Signal")
|
|
31
31
|
local Symbol = require("Symbol")
|
|
32
32
|
local Table = require("Table")
|
|
33
33
|
|
|
@@ -1082,4 +1082,4 @@ function DataStoreStage._checkIntegrity(self: DataStoreStage)
|
|
|
1082
1082
|
end
|
|
1083
1083
|
end
|
|
1084
1084
|
|
|
1085
|
-
return DataStoreStage
|
|
1085
|
+
return DataStoreStage
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
|
|
8
8
|
local require = require(script.Parent.loader).load(script)
|
|
9
9
|
|
|
10
|
-
local Table = require("Table")
|
|
11
10
|
local DataStoreDeleteToken = require("DataStoreDeleteToken")
|
|
12
|
-
local Symbol = require("Symbol")
|
|
13
|
-
local Set = require("Set")
|
|
14
11
|
local DataStoreSnapshotUtils = require("DataStoreSnapshotUtils")
|
|
12
|
+
local Set = require("Set")
|
|
13
|
+
local Symbol = require("Symbol")
|
|
14
|
+
local Table = require("Table")
|
|
15
15
|
|
|
16
16
|
local UNSET_TOKEN = Symbol.named("unsetValue")
|
|
17
17
|
|
|
@@ -328,4 +328,4 @@ function DataStoreWriter.IsCompleteWipe(self: DataStoreWriter): boolean
|
|
|
328
328
|
return false
|
|
329
329
|
end
|
|
330
330
|
|
|
331
|
-
return DataStoreWriter
|
|
331
|
+
return DataStoreWriter
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
DataStore manager for player that automatically saves on player leave and game close.
|
|
3
4
|
|
|
@@ -55,20 +56,21 @@ local RunService = game:GetService("RunService")
|
|
|
55
56
|
|
|
56
57
|
local BaseObject = require("BaseObject")
|
|
57
58
|
local DataStore = require("DataStore")
|
|
58
|
-
local PromiseUtils = require("PromiseUtils")
|
|
59
|
-
local PendingPromiseTracker = require("PendingPromiseTracker")
|
|
60
59
|
local Maid = require("Maid")
|
|
60
|
+
local PendingPromiseTracker = require("PendingPromiseTracker")
|
|
61
61
|
local Promise = require("Promise")
|
|
62
|
+
local PromiseUtils = require("PromiseUtils")
|
|
62
63
|
|
|
63
64
|
local PlayerDataStoreManager = setmetatable({}, BaseObject)
|
|
64
65
|
PlayerDataStoreManager.ClassName = "PlayerDataStoreManager"
|
|
65
66
|
PlayerDataStoreManager.__index = PlayerDataStoreManager
|
|
66
67
|
|
|
68
|
+
export type KeyGenerator = (Player) -> string
|
|
67
69
|
export type PlayerDataStoreManager = typeof(setmetatable(
|
|
68
70
|
{} :: {
|
|
69
71
|
_robloxDataStore: any,
|
|
70
|
-
_keyGenerator:
|
|
71
|
-
_datastores: { [Player]: DataStore },
|
|
72
|
+
_keyGenerator: KeyGenerator,
|
|
73
|
+
_datastores: { [Player]: DataStore.DataStore },
|
|
72
74
|
_removing: { [Player]: boolean },
|
|
73
75
|
_pendingSaves: PendingPromiseTracker.PendingPromiseTracker<any>,
|
|
74
76
|
_removingCallbacks: { (Player) -> any },
|
|
@@ -85,8 +87,12 @@ export type PlayerDataStoreManager = typeof(setmetatable(
|
|
|
85
87
|
@param skipBindingToClose boolean?
|
|
86
88
|
@return PlayerDataStoreManager
|
|
87
89
|
]=]
|
|
88
|
-
function PlayerDataStoreManager.new(
|
|
89
|
-
|
|
90
|
+
function PlayerDataStoreManager.new(
|
|
91
|
+
robloxDataStore: DataStore,
|
|
92
|
+
keyGenerator: KeyGenerator,
|
|
93
|
+
skipBindingToClose: boolean?
|
|
94
|
+
): PlayerDataStoreManager
|
|
95
|
+
local self: PlayerDataStoreManager = setmetatable(BaseObject.new() :: any, PlayerDataStoreManager)
|
|
90
96
|
|
|
91
97
|
assert(type(skipBindingToClose) == "boolean" or skipBindingToClose == nil, "Bad skipBindingToClose")
|
|
92
98
|
|
|
@@ -124,7 +130,7 @@ end
|
|
|
124
130
|
--[=[
|
|
125
131
|
For if you want to disable saving in studio for faster close time!
|
|
126
132
|
]=]
|
|
127
|
-
function PlayerDataStoreManager
|
|
133
|
+
function PlayerDataStoreManager.DisableSaveOnCloseStudio(self: PlayerDataStoreManager): ()
|
|
128
134
|
assert(RunService:IsStudio())
|
|
129
135
|
|
|
130
136
|
self._disableSavingInStudio = true
|
|
@@ -134,7 +140,7 @@ end
|
|
|
134
140
|
Adds a callback to be called before save on removal
|
|
135
141
|
@param callback function -- May return a promise
|
|
136
142
|
]=]
|
|
137
|
-
function PlayerDataStoreManager
|
|
143
|
+
function PlayerDataStoreManager.AddRemovingCallback(self: PlayerDataStoreManager, callback)
|
|
138
144
|
table.insert(self._removingCallbacks, callback)
|
|
139
145
|
end
|
|
140
146
|
|
|
@@ -144,15 +150,21 @@ end
|
|
|
144
150
|
|
|
145
151
|
@param player Player
|
|
146
152
|
]=]
|
|
147
|
-
function PlayerDataStoreManager
|
|
153
|
+
function PlayerDataStoreManager.RemovePlayerDataStore(self: PlayerDataStoreManager, player: Player): ()
|
|
148
154
|
self:_removePlayerDataStore(player)
|
|
149
155
|
end
|
|
150
156
|
|
|
151
157
|
--[=[
|
|
158
|
+
Gets the datastore for a player. If it does not exist, it will create one.
|
|
159
|
+
|
|
160
|
+
:::tip
|
|
161
|
+
Returns nil if the player is in the process of being removed.
|
|
162
|
+
:::
|
|
163
|
+
|
|
152
164
|
@param player Player
|
|
153
|
-
@return DataStore
|
|
165
|
+
@return DataStore?
|
|
154
166
|
]=]
|
|
155
|
-
function PlayerDataStoreManager
|
|
167
|
+
function PlayerDataStoreManager.GetDataStore(self: PlayerDataStoreManager, player: Player): DataStore.DataStore?
|
|
156
168
|
assert(typeof(player) == "Instance", "Bad player")
|
|
157
169
|
assert(player:IsA("Player"), "Bad player")
|
|
158
170
|
|
|
@@ -173,14 +185,14 @@ end
|
|
|
173
185
|
resolves when all pending saves are saved.
|
|
174
186
|
@return Promise
|
|
175
187
|
]=]
|
|
176
|
-
function PlayerDataStoreManager
|
|
188
|
+
function PlayerDataStoreManager.PromiseAllSaves(self: PlayerDataStoreManager): Promise.Promise<()>
|
|
177
189
|
for player, _ in self._datastores do
|
|
178
190
|
self:_removePlayerDataStore(player)
|
|
179
191
|
end
|
|
180
192
|
return self._maid:GivePromise(PromiseUtils.all(self._pendingSaves:GetAll()))
|
|
181
193
|
end
|
|
182
194
|
|
|
183
|
-
function PlayerDataStoreManager
|
|
195
|
+
function PlayerDataStoreManager._createDataStore(self: PlayerDataStoreManager, player: Player): DataStore.DataStore
|
|
184
196
|
assert(not self._datastores[player], "Bad player")
|
|
185
197
|
|
|
186
198
|
local datastore = DataStore.new(self._robloxDataStore, self:_getKey(player))
|
|
@@ -195,7 +207,7 @@ function PlayerDataStoreManager:_createDataStore(player: Player)
|
|
|
195
207
|
return datastore
|
|
196
208
|
end
|
|
197
209
|
|
|
198
|
-
function PlayerDataStoreManager
|
|
210
|
+
function PlayerDataStoreManager._removePlayerDataStore(self: PlayerDataStoreManager, player: Player)
|
|
199
211
|
assert(typeof(player) == "Instance", "Bad player")
|
|
200
212
|
assert(player:IsA("Player"), "Bad player")
|
|
201
213
|
|
|
@@ -228,8 +240,8 @@ function PlayerDataStoreManager:_removePlayerDataStore(player: Player)
|
|
|
228
240
|
self._maid._savingConns[player] = nil
|
|
229
241
|
end
|
|
230
242
|
|
|
231
|
-
function PlayerDataStoreManager
|
|
243
|
+
function PlayerDataStoreManager._getKey(self: PlayerDataStoreManager, player: Player)
|
|
232
244
|
return self._keyGenerator(player)
|
|
233
245
|
end
|
|
234
246
|
|
|
235
|
-
return PlayerDataStoreManager
|
|
247
|
+
return PlayerDataStoreManager
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Centralized service using serviceBag. This will let other packages work with a single player datastore service.
|
|
3
4
|
|
|
@@ -7,18 +8,19 @@
|
|
|
7
8
|
|
|
8
9
|
local require = require(script.Parent.loader).load(script)
|
|
9
10
|
|
|
10
|
-
local
|
|
11
|
+
local DataStore = require("DataStore")
|
|
11
12
|
local DataStorePromises = require("DataStorePromises")
|
|
12
|
-
local Promise = require("Promise")
|
|
13
13
|
local Maid = require("Maid")
|
|
14
|
-
local
|
|
14
|
+
local PlayerDataStoreManager = require("PlayerDataStoreManager")
|
|
15
|
+
local Promise = require("Promise")
|
|
16
|
+
local ServiceBag = require("ServiceBag")
|
|
15
17
|
|
|
16
18
|
local PlayerDataStoreService = {}
|
|
17
19
|
PlayerDataStoreService.ServiceName = "PlayerDataStoreService"
|
|
18
20
|
|
|
19
21
|
export type PlayerDataStoreService = typeof(setmetatable(
|
|
20
22
|
{} :: {
|
|
21
|
-
_serviceBag:
|
|
23
|
+
_serviceBag: ServiceBag.ServiceBag,
|
|
22
24
|
_maid: Maid.Maid,
|
|
23
25
|
_dataStoreName: string,
|
|
24
26
|
_dataStoreScope: string,
|
|
@@ -31,7 +33,7 @@ export type PlayerDataStoreService = typeof(setmetatable(
|
|
|
31
33
|
Initializes the PlayerDataStoreService. Should be done via [ServiceBag.Init].
|
|
32
34
|
@param serviceBag ServiceBag
|
|
33
35
|
]=]
|
|
34
|
-
function PlayerDataStoreService:Init(serviceBag:
|
|
36
|
+
function PlayerDataStoreService:Init(serviceBag: ServiceBag.ServiceBag)
|
|
35
37
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
36
38
|
self._maid = Maid.new()
|
|
37
39
|
|
|
@@ -61,7 +63,7 @@ end
|
|
|
61
63
|
|
|
62
64
|
@param dataStoreName string
|
|
63
65
|
]=]
|
|
64
|
-
function PlayerDataStoreService:SetDataStoreName(dataStoreName: string)
|
|
66
|
+
function PlayerDataStoreService:SetDataStoreName(dataStoreName: string): ()
|
|
65
67
|
assert(type(dataStoreName) == "string", "Bad dataStoreName")
|
|
66
68
|
assert(self._promiseStarted, "Not initialized")
|
|
67
69
|
assert(self._promiseStarted:IsPending(), "Already started, cannot configure")
|
|
@@ -78,7 +80,7 @@ end
|
|
|
78
80
|
|
|
79
81
|
@param dataStoreScope string
|
|
80
82
|
]=]
|
|
81
|
-
function PlayerDataStoreService:SetDataStoreScope(dataStoreScope: string)
|
|
83
|
+
function PlayerDataStoreService:SetDataStoreScope(dataStoreScope: string): ()
|
|
82
84
|
assert(type(dataStoreScope) == "string", "Bad dataStoreScope")
|
|
83
85
|
assert(self._promiseStarted, "Not initialized")
|
|
84
86
|
assert(self._promiseStarted:IsPending(), "Already started, cannot configure")
|
|
@@ -91,7 +93,7 @@ end
|
|
|
91
93
|
@param player Player
|
|
92
94
|
@return Promise<DataStore>
|
|
93
95
|
]=]
|
|
94
|
-
function PlayerDataStoreService:PromiseDataStore(player: Player)
|
|
96
|
+
function PlayerDataStoreService:PromiseDataStore(player: Player): Promise.Promise<DataStore.DataStore>
|
|
95
97
|
return self:PromiseManager():Then(function(manager)
|
|
96
98
|
return manager:GetDataStore(player)
|
|
97
99
|
end)
|
|
@@ -110,9 +112,9 @@ end
|
|
|
110
112
|
|
|
111
113
|
--[=[
|
|
112
114
|
Retrieves the manager
|
|
113
|
-
@return PlayerDataStoreManager
|
|
115
|
+
@return Promise<PlayerDataStoreManager>
|
|
114
116
|
]=]
|
|
115
|
-
function PlayerDataStoreService:PromiseManager()
|
|
117
|
+
function PlayerDataStoreService:PromiseManager(): Promise.Promise<PlayerDataStoreManager.PlayerDataStoreManager>
|
|
116
118
|
if self._dataStoreManagerPromise then
|
|
117
119
|
return self._dataStoreManagerPromise
|
|
118
120
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Service which manages central access to datastore. This datastore is per a private server
|
|
3
4
|
or reserved server. The main server will also get one, with an empty key.
|
|
@@ -10,12 +11,13 @@ local require = require(script.Parent.loader).load(script)
|
|
|
10
11
|
local DataStore = require("DataStore")
|
|
11
12
|
local DataStorePromises = require("DataStorePromises")
|
|
12
13
|
local Maid = require("Maid")
|
|
13
|
-
local
|
|
14
|
+
local Promise = require("Promise")
|
|
15
|
+
local ServiceBag = require("ServiceBag")
|
|
14
16
|
|
|
15
17
|
local PrivateServerDataStoreService = {}
|
|
16
18
|
PrivateServerDataStoreService.ServiceName = "PrivateServerDataStoreService"
|
|
17
19
|
|
|
18
|
-
function PrivateServerDataStoreService:Init(serviceBag:
|
|
20
|
+
function PrivateServerDataStoreService:Init(serviceBag: ServiceBag.ServiceBag)
|
|
19
21
|
assert(not self._serviceBag, "Already initialized")
|
|
20
22
|
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
21
23
|
|
|
@@ -24,48 +26,51 @@ function PrivateServerDataStoreService:Init(serviceBag: _ServiceBag.ServiceBag)
|
|
|
24
26
|
self._maid = Maid.new()
|
|
25
27
|
end
|
|
26
28
|
|
|
27
|
-
function PrivateServerDataStoreService:PromiseDataStore()
|
|
29
|
+
function PrivateServerDataStoreService:PromiseDataStore(): Promise.Promise<DataStore.DataStore>
|
|
28
30
|
if self._dataStorePromise then
|
|
29
31
|
return self._dataStorePromise
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
self._dataStorePromise = self:_promiseRobloxDataStore()
|
|
33
|
-
:
|
|
34
|
-
|
|
35
|
-
self._maid:GiveTask(dataStore)
|
|
34
|
+
self._dataStorePromise = self:_promiseRobloxDataStore():Then(function(robloxDataStore)
|
|
35
|
+
local dataStore = DataStore.new(robloxDataStore, self:_getKey())
|
|
36
|
+
self._maid:GiveTask(dataStore)
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if game.PrivateServerOwnerId ~= 0 then
|
|
39
|
+
dataStore:Store("LastPrivateServerOwnerId", game.PrivateServerOwnerId)
|
|
40
|
+
end
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
self._maid:GiveTask(self._bindToCloseService:RegisterPromiseOnCloseCallback(function()
|
|
43
|
+
return dataStore:Save()
|
|
44
|
+
end))
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
return dataStore
|
|
47
|
+
end)
|
|
47
48
|
|
|
48
49
|
return self._dataStorePromise
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
function PrivateServerDataStoreService:SetCustomKey(customKey)
|
|
52
|
-
assert(
|
|
52
|
+
function PrivateServerDataStoreService:SetCustomKey(customKey: string)
|
|
53
|
+
assert(
|
|
54
|
+
self._dataStorePromise == nil,
|
|
55
|
+
"[PrivateServerDataStoreService] - Already got datastore, cannot set custom key"
|
|
56
|
+
)
|
|
53
57
|
|
|
54
58
|
self._customKey = customKey
|
|
55
59
|
end
|
|
56
60
|
|
|
57
|
-
function PrivateServerDataStoreService:_promiseRobloxDataStore()
|
|
61
|
+
function PrivateServerDataStoreService:_promiseRobloxDataStore(): Promise.Promise<any>
|
|
58
62
|
if self._robloxDataStorePromise then
|
|
59
63
|
return self._robloxDataStorePromise
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
-- This could potentially
|
|
63
|
-
self._robloxDataStorePromise =
|
|
67
|
+
self._robloxDataStorePromise =
|
|
68
|
+
self._maid:GivePromise(DataStorePromises.promiseDataStore("PrivateServerDataStores", "Version1"))
|
|
64
69
|
|
|
65
70
|
return self._robloxDataStorePromise
|
|
66
71
|
end
|
|
67
72
|
|
|
68
|
-
function PrivateServerDataStoreService:_getKey()
|
|
73
|
+
function PrivateServerDataStoreService:_getKey(): string
|
|
69
74
|
if self._customKey then
|
|
70
75
|
return self._customKey
|
|
71
76
|
end
|
|
@@ -76,8 +81,8 @@ function PrivateServerDataStoreService:_getKey()
|
|
|
76
81
|
end
|
|
77
82
|
end
|
|
78
83
|
|
|
79
|
-
function PrivateServerDataStoreService:Destroy()
|
|
84
|
+
function PrivateServerDataStoreService:Destroy(): ()
|
|
80
85
|
self._maid:DoCleaning()
|
|
81
86
|
end
|
|
82
87
|
|
|
83
|
-
return PrivateServerDataStoreService
|
|
88
|
+
return PrivateServerDataStoreService
|
|
@@ -9,8 +9,8 @@ local require = require(script.Parent.loader).load(script)
|
|
|
9
9
|
|
|
10
10
|
local DataStoreService = game:GetService("DataStoreService")
|
|
11
11
|
|
|
12
|
-
local Promise = require("Promise")
|
|
13
12
|
local PagesUtils = require("PagesUtils")
|
|
13
|
+
local Promise = require("Promise")
|
|
14
14
|
local Table = require("Table")
|
|
15
15
|
|
|
16
16
|
local DataStorePromises = {}
|
|
@@ -260,7 +260,6 @@ local function areEquivalentPageData(data: { OrderedDataStoreEntry }, otherData:
|
|
|
260
260
|
return Table.deepEquivalent(map, otherMap)
|
|
261
261
|
end
|
|
262
262
|
|
|
263
|
-
|
|
264
263
|
--[=[
|
|
265
264
|
Returns a DataStorePages object. The sort order is determined by ascending,
|
|
266
265
|
the length of each page by pageSize, and minValue/maxValue are
|
|
@@ -274,7 +273,14 @@ end
|
|
|
274
273
|
@param maxValue number?
|
|
275
274
|
@return Promise<OrderedDataStoreEntry>
|
|
276
275
|
]=]
|
|
277
|
-
function DataStorePromises.promiseOrderedEntries(
|
|
276
|
+
function DataStorePromises.promiseOrderedEntries(
|
|
277
|
+
orderedDataStore: OrderedDataStore,
|
|
278
|
+
ascending: boolean,
|
|
279
|
+
pagesize: number,
|
|
280
|
+
entries: number,
|
|
281
|
+
minValue: number?,
|
|
282
|
+
maxValue: number?
|
|
283
|
+
): Promise.Promise<OrderedDataStoreEntry>
|
|
278
284
|
assert(typeof(orderedDataStore) == "Instance" and orderedDataStore:IsA("OrderedDataStore"), "Bad orderedDataStore")
|
|
279
285
|
assert(type(ascending) == "boolean", "Bad ascending")
|
|
280
286
|
assert(type(entries) == "number", "Bad entries")
|
|
@@ -322,4 +328,4 @@ function DataStorePromises.promiseOrderedEntries(orderedDataStore: OrderedDataSt
|
|
|
322
328
|
end)
|
|
323
329
|
end
|
|
324
330
|
|
|
325
|
-
return DataStorePromises
|
|
331
|
+
return DataStorePromises
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Utility methods to validate strings which can be used to attack datastores in a string saving attack otherwise.
|
|
3
4
|
|
|
@@ -12,7 +13,7 @@ local DataStoreStringUtils = {}
|
|
|
12
13
|
@param str string
|
|
13
14
|
@return boolean
|
|
14
15
|
]=]
|
|
15
|
-
function DataStoreStringUtils.isValidUTF8(str)
|
|
16
|
+
function DataStoreStringUtils.isValidUTF8(str: string): (boolean, string?)
|
|
16
17
|
if type(str) ~= "string" then
|
|
17
18
|
return false, "Not a string"
|
|
18
19
|
end
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
@class ServerMain
|
|
3
3
|
]]
|
|
4
4
|
|
|
5
|
-
local ServerScriptService = game:GetService("ServerScriptService")
|
|
6
5
|
local HttpService = game:GetService("HttpService")
|
|
6
|
+
local ServerScriptService = game:GetService("ServerScriptService")
|
|
7
7
|
|
|
8
8
|
local loader = ServerScriptService:FindFirstChild("LoaderUtils", true).Parent
|
|
9
9
|
local require = require(loader).bootstrapGame(ServerScriptService.datastore)
|
|
@@ -48,7 +48,7 @@ local function spinUpGameCopy(prefix: string)
|
|
|
48
48
|
-- print(string.format("(%s) dataStore:Observe()", prefix), viewSnapshot)
|
|
49
49
|
-- end))
|
|
50
50
|
|
|
51
|
-
task.delay(4*TURN_TIME, function()
|
|
51
|
+
task.delay(4 * TURN_TIME, function()
|
|
52
52
|
warn("Blue server is restoring data")
|
|
53
53
|
|
|
54
54
|
substore:Store(guid, true)
|
|
@@ -71,8 +71,8 @@ local function spinUpGameCopy(prefix: string)
|
|
|
71
71
|
warn(string.format("%s server is adding substore data", prefix))
|
|
72
72
|
|
|
73
73
|
substore:Store(guid, {
|
|
74
|
-
playerCount = 5
|
|
75
|
-
startTime = DateTime.now().UnixTimestamp
|
|
74
|
+
playerCount = 5,
|
|
75
|
+
startTime = DateTime.now().UnixTimestamp,
|
|
76
76
|
})
|
|
77
77
|
-- dataStore:Save()
|
|
78
78
|
|
|
@@ -109,7 +109,6 @@ local function spinUpGameCopy(prefix: string)
|
|
|
109
109
|
substore:Delete(guid)
|
|
110
110
|
return Promise.resolved()
|
|
111
111
|
end))
|
|
112
|
-
|
|
113
112
|
end)
|
|
114
113
|
|
|
115
114
|
return maid
|
|
@@ -117,4 +116,4 @@ end
|
|
|
117
116
|
|
|
118
117
|
spinUpGameCopy("red")
|
|
119
118
|
spinUpGameCopy("blue")
|
|
120
|
-
spinUpGameCopy("green")
|
|
119
|
+
spinUpGameCopy("green")
|