@quenty/loader 5.0.1 → 5.1.0-canary.288.30808b9.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 +2 -2
- package/src/LoaderUtils.lua +2 -2
- package/src/PackageInfoUtils.lua +1 -1
- package/src/ScriptInfoUtils.lua +2 -2
- package/src/StaticLegacyLoader.lua +2 -2
- package/src2/Bounce/BounceTemplate.lua +17 -0
- package/src2/Bounce/BounceTemplateUtils.lua +51 -0
- package/src2/Dependencies/DependencyUtils.lua +133 -0
- package/src2/Loader/LoaderAdder.lua +181 -0
- package/src2/LoaderUtils.lua +0 -0
- package/src2/Maid.lua +222 -0
- package/src2/PathUtils.lua +15 -0
- package/src2/Replication/ReplicationType.lua +13 -0
- package/src2/Replication/ReplicationTypeUtils.lua +37 -0
- package/src2/Replication/Replicator.lua +549 -0
- package/src2/Replication/ReplicatorReferences.lua +103 -0
- package/src2/Replication/ReplicatorUtils.lua +24 -0
- package/src2/Utils.lua +114 -0
- package/src2/init.lua +68 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
Monitors dependencies primarily for replication. Handles the following scenarios.
|
|
3
|
+
|
|
4
|
+
This system dynamically replicates whatever state exists in the tree except we
|
|
5
|
+
filter out server-specific assets while any client-side assets are still replicated
|
|
6
|
+
even if deeper in the tree.
|
|
7
|
+
|
|
8
|
+
By separating out the replication component of the loader from the loading logic
|
|
9
|
+
we can more easily support hot reloading and future loading scenarios.
|
|
10
|
+
|
|
11
|
+
Repliation rules:
|
|
12
|
+
1. Replicate the whole tree, including any changes
|
|
13
|
+
2. Module scripts named Server are replaced with a folder
|
|
14
|
+
3. Module scripts that are in server mode won't replicate unless a client dependency is needed or found.
|
|
15
|
+
4. Once we hit a "Template" object we stop trying to be smart since Mesh parts are not API accessible.
|
|
16
|
+
5. References are preserved for ObjectValues.
|
|
17
|
+
|
|
18
|
+
This system is designed to minimize changes such that hot reloading can be easily
|
|
19
|
+
implemented.
|
|
20
|
+
|
|
21
|
+
Right now it fails to be performance friendly with module scripts under another
|
|
22
|
+
module script.
|
|
23
|
+
|
|
24
|
+
@class Replicator
|
|
25
|
+
]=]
|
|
26
|
+
|
|
27
|
+
local Maid = require(script.Parent.Parent.Maid)
|
|
28
|
+
local ReplicationType = require(script.Parent.ReplicationType)
|
|
29
|
+
local ReplicationTypeUtils = require(script.Parent.ReplicationTypeUtils)
|
|
30
|
+
local ReplicatorUtils = require(script.Parent.ReplicatorUtils)
|
|
31
|
+
local ReplicatorReferences = require(script.Parent.ReplicatorReferences)
|
|
32
|
+
|
|
33
|
+
local Replicator = {}
|
|
34
|
+
Replicator.ClassName = "Replicator"
|
|
35
|
+
Replicator.__index = Replicator
|
|
36
|
+
|
|
37
|
+
--[=[
|
|
38
|
+
Constructs a new Replicator which will do the syncing.
|
|
39
|
+
|
|
40
|
+
@param references ReplicatorReferences
|
|
41
|
+
@return Replicator
|
|
42
|
+
]=]
|
|
43
|
+
function Replicator.new(references)
|
|
44
|
+
local self = setmetatable({}, Replicator)
|
|
45
|
+
|
|
46
|
+
assert(ReplicatorReferences.isReplicatorReferences(references), "Bad references")
|
|
47
|
+
|
|
48
|
+
self._maid = Maid.new()
|
|
49
|
+
self._references = references
|
|
50
|
+
|
|
51
|
+
self._target = Instance.new("ObjectValue")
|
|
52
|
+
self._target.Value = nil
|
|
53
|
+
self._maid:GiveTask(self._target)
|
|
54
|
+
|
|
55
|
+
self._replicatedDescendantCount = Instance.new("IntValue")
|
|
56
|
+
self._replicatedDescendantCount.Value = 0
|
|
57
|
+
self._maid:GiveTask(self._replicatedDescendantCount)
|
|
58
|
+
self._hasReplicatedDescendants = Instance.new("BoolValue")
|
|
59
|
+
self._hasReplicatedDescendants.Value = false
|
|
60
|
+
self._maid:GiveTask(self._hasReplicatedDescendants)
|
|
61
|
+
|
|
62
|
+
self._replicationType = Instance.new("StringValue")
|
|
63
|
+
self._replicationType.Value = ReplicationType.SHARED
|
|
64
|
+
self._maid:GiveTask(self._replicationType)
|
|
65
|
+
|
|
66
|
+
self._maid:GiveTask(self._replicatedDescendantCount.Changed:Connect(function()
|
|
67
|
+
self._hasReplicatedDescendants.Value = self._replicatedDescendantCount.Value > 0
|
|
68
|
+
end))
|
|
69
|
+
|
|
70
|
+
return self
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
--[=[
|
|
74
|
+
Replicates children from the given root
|
|
75
|
+
|
|
76
|
+
@param root Instance
|
|
77
|
+
]=]
|
|
78
|
+
function Replicator:ReplicateFrom(root)
|
|
79
|
+
assert(typeof(root) == "Instance", "Bad root")
|
|
80
|
+
|
|
81
|
+
assert(not self._replicationStarted, "Already bound events")
|
|
82
|
+
self._replicationStarted = true
|
|
83
|
+
|
|
84
|
+
self._maid:GiveTask(root.ChildAdded:Connect(function(child)
|
|
85
|
+
self:_handleChildAdded(child)
|
|
86
|
+
end))
|
|
87
|
+
self._maid:GiveTask(root.ChildRemoved:Connect(function(child)
|
|
88
|
+
self:_handleChildRemoved(child)
|
|
89
|
+
end))
|
|
90
|
+
for _, child in pairs(root:GetChildren()) do
|
|
91
|
+
self:_handleChildAdded(child)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
--[=[
|
|
96
|
+
Returns true if the argument is a replicator
|
|
97
|
+
|
|
98
|
+
@param replicator any?
|
|
99
|
+
@return boolean
|
|
100
|
+
]=]
|
|
101
|
+
function Replicator.isReplicator(replicator)
|
|
102
|
+
return type(replicator) == "table" and
|
|
103
|
+
getmetatable(replicator) == Replicator
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
--[=[
|
|
107
|
+
Returns the replicated descendant count value.
|
|
108
|
+
@return IntValue
|
|
109
|
+
]=]
|
|
110
|
+
function Replicator:GetReplicatedDescendantCountValue()
|
|
111
|
+
return self._replicatedDescendantCount
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
--[=[
|
|
115
|
+
Sets the replication type for this replicator
|
|
116
|
+
|
|
117
|
+
@param replicationType ReplicationType
|
|
118
|
+
]=]
|
|
119
|
+
function Replicator:SetReplicationType(replicationType)
|
|
120
|
+
assert(ReplicationTypeUtils.isReplicationType(replicationType), "Bad replicationType")
|
|
121
|
+
|
|
122
|
+
self._replicationType.Value = replicationType
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
--[=[
|
|
126
|
+
Sets the target for the replicator where the results will be parented.
|
|
127
|
+
|
|
128
|
+
@param target Instance?
|
|
129
|
+
]=]
|
|
130
|
+
function Replicator:SetTarget(target)
|
|
131
|
+
assert(typeof(target) == "Instance" or target == nil, "Bad target")
|
|
132
|
+
|
|
133
|
+
self._target.Value = target
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
--[=[
|
|
137
|
+
Gets the current target for the replicator.
|
|
138
|
+
|
|
139
|
+
@return Instance?
|
|
140
|
+
]=]
|
|
141
|
+
function Replicator:GetTarget()
|
|
142
|
+
return self._target.Value
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
--[=[
|
|
146
|
+
Gets a value representing if there's any replicated children. Used to
|
|
147
|
+
avoid leaking more server-side information than needed for the user.
|
|
148
|
+
|
|
149
|
+
@return BoolValue
|
|
150
|
+
]=]
|
|
151
|
+
function Replicator:GetHasReplicatedChildrenValue()
|
|
152
|
+
return self._hasReplicatedDescendants
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
function Replicator:GetReplicationTypeValue()
|
|
156
|
+
return self._replicationType
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
function Replicator:_handleChildRemoved(child)
|
|
160
|
+
self._maid[child] = nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
function Replicator:_handleChildAdded(child)
|
|
164
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
165
|
+
|
|
166
|
+
local maid = Maid.new()
|
|
167
|
+
|
|
168
|
+
if child.Archivable then
|
|
169
|
+
maid._current = self:_renderChild(child)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
maid:GiveTask(child:GetPropertyChangedSignal("Archivable"):Connect(function()
|
|
173
|
+
if child.Archivable then
|
|
174
|
+
maid._current = self:_renderChild(child)
|
|
175
|
+
else
|
|
176
|
+
maid._current = nil
|
|
177
|
+
end
|
|
178
|
+
end))
|
|
179
|
+
|
|
180
|
+
self._maid[child] = maid
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
function Replicator:_renderChild(child)
|
|
184
|
+
local maid = Maid.new()
|
|
185
|
+
|
|
186
|
+
local replicator = Replicator.new(self._references)
|
|
187
|
+
self:_setupReplicatorDescendantCount(maid, replicator)
|
|
188
|
+
maid:GiveTask(replicator)
|
|
189
|
+
|
|
190
|
+
if child:IsA("Folder") then
|
|
191
|
+
self:_setupReplicatorTypeFromFolderName(maid, replicator, child)
|
|
192
|
+
else
|
|
193
|
+
self:_setupReplicatorType(maid, replicator)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
local replicationTypeValue = replicator:GetReplicationTypeValue()
|
|
197
|
+
maid._current = self:_replicateBasedUponMode(replicator, replicationTypeValue.Value, child)
|
|
198
|
+
maid:GiveTask(replicationTypeValue.Changed:Connect(function()
|
|
199
|
+
maid._current = nil
|
|
200
|
+
maid._current = self:_replicateBasedUponMode(replicator, replicationTypeValue.Value, child)
|
|
201
|
+
end))
|
|
202
|
+
|
|
203
|
+
replicator:ReplicateFrom(child)
|
|
204
|
+
|
|
205
|
+
return maid
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
function Replicator:_replicateBasedUponMode(replicator, replicationType, child)
|
|
210
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
211
|
+
assert(ReplicationTypeUtils.isReplicationType(replicationType), "Bad replicationType")
|
|
212
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
213
|
+
|
|
214
|
+
if replicationType == ReplicationType.SERVER then
|
|
215
|
+
return self:_doReplicationServer(replicator, child)
|
|
216
|
+
elseif replicationType == ReplicationType.SHARED
|
|
217
|
+
or replicationType == ReplicationType.CLIENT then
|
|
218
|
+
return self:_doReplicationClient(replicator, child)
|
|
219
|
+
else
|
|
220
|
+
error("[Replicator] - Unknown replicationType")
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
function Replicator:_doReplicationServer(replicator, child)
|
|
225
|
+
local maid = Maid.new()
|
|
226
|
+
|
|
227
|
+
local hasReplicatedChildren = replicator:GetHasReplicatedChildrenValue()
|
|
228
|
+
maid:GiveTask(hasReplicatedChildren.Changed:Connect(function()
|
|
229
|
+
if hasReplicatedChildren.Value then
|
|
230
|
+
maid._current = nil
|
|
231
|
+
maid._current = self:_doServerClone(replicator, child)
|
|
232
|
+
else
|
|
233
|
+
maid._current = nil
|
|
234
|
+
end
|
|
235
|
+
end))
|
|
236
|
+
|
|
237
|
+
if hasReplicatedChildren.Value then
|
|
238
|
+
maid._current = self:_doServerClone(replicator, child)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
function Replicator:_doServerClone(replicator, child)
|
|
243
|
+
-- Always a folder to prevent information from leaking...
|
|
244
|
+
local maid = Maid.new()
|
|
245
|
+
local copy = Instance.new("Folder")
|
|
246
|
+
|
|
247
|
+
self:_setupNameReplication(maid, child, copy)
|
|
248
|
+
self:_setupParentReplication(maid, copy)
|
|
249
|
+
self:_setupReference(maid, child, copy)
|
|
250
|
+
maid:GiveTask(copy)
|
|
251
|
+
|
|
252
|
+
-- Setup replication for this specific instance.
|
|
253
|
+
self:_setupReplicatorTarget(maid, replicator, copy)
|
|
254
|
+
|
|
255
|
+
return maid
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
function Replicator:_doReplicationClient(replicator, child)
|
|
259
|
+
local maid = Maid.new()
|
|
260
|
+
|
|
261
|
+
if child:IsA("ModuleScript") then
|
|
262
|
+
self:_setupReplicatedDescendantCountAdd(maid, 1)
|
|
263
|
+
|
|
264
|
+
maid._current = self:_doModuleScriptCloneClient(replicator, child)
|
|
265
|
+
maid:GiveTask(child.Changed:Connect(function(property)
|
|
266
|
+
if property == "Source" then
|
|
267
|
+
maid._current = nil
|
|
268
|
+
maid._current = self:_doModuleScriptCloneClient(replicator, child)
|
|
269
|
+
end
|
|
270
|
+
end))
|
|
271
|
+
elseif child:IsA("Folder") then
|
|
272
|
+
local copy = Instance.new("Folder")
|
|
273
|
+
|
|
274
|
+
self:_doStandardReplication(maid, replicator, child, copy)
|
|
275
|
+
|
|
276
|
+
elseif child:IsA("ObjectValue") then
|
|
277
|
+
local copy = Instance.new("ObjectValue")
|
|
278
|
+
|
|
279
|
+
self:_setupObjectValueReplication(maid, child, copy)
|
|
280
|
+
self:_doStandardReplication(maid, replicator, child, copy)
|
|
281
|
+
else
|
|
282
|
+
local copy = ReplicatorUtils.cloneWithoutChildren(child)
|
|
283
|
+
|
|
284
|
+
-- TODO: Maybe do better
|
|
285
|
+
self:_setupReplicatedDescendantCountAdd(maid, 1)
|
|
286
|
+
self:_doStandardReplication(maid, replicator, child, copy)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
return maid
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
function Replicator:_doModuleScriptCloneClient(replicator, child)
|
|
293
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
294
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
295
|
+
|
|
296
|
+
local copy = ReplicatorUtils.cloneWithoutChildren(child)
|
|
297
|
+
local maid = Maid.new()
|
|
298
|
+
|
|
299
|
+
self:_doStandardReplication(maid, replicator, child, copy)
|
|
300
|
+
|
|
301
|
+
return maid
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
function Replicator:_doStandardReplication(maid, replicator, child, copy)
|
|
305
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
306
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
307
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
308
|
+
|
|
309
|
+
self:_setupAttributeReplication(maid, child, copy)
|
|
310
|
+
self:_setupNameReplication(maid, child, copy)
|
|
311
|
+
self:_setupParentReplication(maid, copy)
|
|
312
|
+
self:_setupReference(maid, child, copy)
|
|
313
|
+
maid:GiveTask(copy)
|
|
314
|
+
|
|
315
|
+
-- Setup replication for this specific instance.
|
|
316
|
+
self:_setupReplicatorTarget(maid, replicator, copy)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
function Replicator:_setupReplicatedDescendantCountAdd(maid, amount)
|
|
320
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
321
|
+
assert(type(amount) == "number", "Bad amount")
|
|
322
|
+
|
|
323
|
+
-- Do this replication count here so when the source changes we don't
|
|
324
|
+
-- have any flickering.
|
|
325
|
+
self._replicatedDescendantCount.Value = self._replicatedDescendantCount.Value + amount
|
|
326
|
+
maid:GiveTask(function()
|
|
327
|
+
self._replicatedDescendantCount.Value = self._replicatedDescendantCount.Value - amount
|
|
328
|
+
end)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
--[[
|
|
332
|
+
Sets up the replicator target so children can be parented into the
|
|
333
|
+
instance.
|
|
334
|
+
|
|
335
|
+
Sets the target to the copy
|
|
336
|
+
|
|
337
|
+
@param maid Maid
|
|
338
|
+
@param replicator Replicator
|
|
339
|
+
@param copy Instance
|
|
340
|
+
]]
|
|
341
|
+
function Replicator:_setupReplicatorTarget(maid, replicator, copy)
|
|
342
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
343
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
344
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
345
|
+
|
|
346
|
+
replicator:SetTarget(copy)
|
|
347
|
+
|
|
348
|
+
maid:GiveTask(function()
|
|
349
|
+
if replicator.Destroy and replicator:GetTarget() == copy then
|
|
350
|
+
replicator:SetTarget(nil)
|
|
351
|
+
end
|
|
352
|
+
end)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
--[[
|
|
356
|
+
Adds the children count of a child replicator to this replicators
|
|
357
|
+
count.
|
|
358
|
+
|
|
359
|
+
We use this to determine if we need to build the whole tree or not.
|
|
360
|
+
|
|
361
|
+
@param maid Maid
|
|
362
|
+
@param replicator Replicator
|
|
363
|
+
]]
|
|
364
|
+
function Replicator:_setupReplicatorDescendantCount(maid, replicator)
|
|
365
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
366
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
367
|
+
|
|
368
|
+
local replicatedChildrenCount = replicator:GetReplicatedDescendantCountValue()
|
|
369
|
+
local lastValue = replicatedChildrenCount.Value
|
|
370
|
+
self._replicatedDescendantCount.Value = self._replicatedDescendantCount.Value + lastValue
|
|
371
|
+
|
|
372
|
+
maid:GiveTask(replicatedChildrenCount.Changed:Connect(function()
|
|
373
|
+
local value = replicatedChildrenCount.Value
|
|
374
|
+
local delta = value - lastValue
|
|
375
|
+
lastValue = value
|
|
376
|
+
self._replicatedDescendantCount.Value = self._replicatedDescendantCount.Value + delta
|
|
377
|
+
end))
|
|
378
|
+
|
|
379
|
+
maid:GiveTask(function()
|
|
380
|
+
local value = lastValue
|
|
381
|
+
lastValue = 0
|
|
382
|
+
self._replicatedDescendantCount.Value = self._replicatedDescendantCount.Value - value
|
|
383
|
+
end)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
--[[
|
|
387
|
+
Sets up references from original to the copy. This allows
|
|
388
|
+
|
|
389
|
+
@param maid Maid
|
|
390
|
+
@param child Instance
|
|
391
|
+
@param copy Instance
|
|
392
|
+
]]
|
|
393
|
+
function Replicator:_setupReference(maid, child, copy)
|
|
394
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
395
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
396
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
397
|
+
|
|
398
|
+
-- Setup references
|
|
399
|
+
self._references:SetReference(child, copy)
|
|
400
|
+
maid:GiveTask(function()
|
|
401
|
+
self._references:UnsetReference(child, copy)
|
|
402
|
+
end)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
--[[
|
|
406
|
+
Sets up replication type from the folder name. This sort of thing controls
|
|
407
|
+
replication hierarchy for instances we want to have.
|
|
408
|
+
|
|
409
|
+
@param maid Maid
|
|
410
|
+
@param replicator
|
|
411
|
+
@param child Instance
|
|
412
|
+
]]
|
|
413
|
+
function Replicator:_setupReplicatorTypeFromFolderName(maid, replicator, child)
|
|
414
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
415
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
416
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
417
|
+
|
|
418
|
+
maid:GiveTask(self._replicationType.Changed:Connect(function()
|
|
419
|
+
replicator:SetReplicationType(self:_getFolderReplicationType(child.Name))
|
|
420
|
+
end))
|
|
421
|
+
maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(function()
|
|
422
|
+
replicator:SetReplicationType(self:_getFolderReplicationType(child.Name))
|
|
423
|
+
end))
|
|
424
|
+
replicator:SetReplicationType(self:_getFolderReplicationType(child.Name))
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
function Replicator:_setupReplicatorType(maid, replicator)
|
|
428
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
429
|
+
assert(Replicator.isReplicator(replicator), "Bad replicator")
|
|
430
|
+
|
|
431
|
+
replicator:SetReplicationType(self._replicationType.Value)
|
|
432
|
+
maid:GiveTask(self._replicationType.Changed:Connect(function()
|
|
433
|
+
replicator:SetReplicationType(self._replicationType.Value)
|
|
434
|
+
end))
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
--[[
|
|
438
|
+
Sets up name replication explicitly.
|
|
439
|
+
|
|
440
|
+
@param maid Maid
|
|
441
|
+
@param child Instance
|
|
442
|
+
@param copy Instance
|
|
443
|
+
]]
|
|
444
|
+
function Replicator:_setupNameReplication(maid, child, copy)
|
|
445
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
446
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
447
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
448
|
+
|
|
449
|
+
copy.Name = child.Name
|
|
450
|
+
maid:GiveTask(child:GetPropertyChangedSignal("Name"):Connect(function()
|
|
451
|
+
copy.Name = child.Name
|
|
452
|
+
end))
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
--[[
|
|
456
|
+
Sets up the parent replication.
|
|
457
|
+
|
|
458
|
+
@param maid Maid
|
|
459
|
+
@param copy Instance
|
|
460
|
+
]]
|
|
461
|
+
function Replicator:_setupParentReplication(maid, copy)
|
|
462
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
463
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
464
|
+
|
|
465
|
+
maid:GiveTask(self._target.Changed:Connect(function()
|
|
466
|
+
copy.Parent = self._target.Value
|
|
467
|
+
end))
|
|
468
|
+
copy.Parent = self._target.Value
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
--[[
|
|
472
|
+
Sets up the object value replication to point towards new values.
|
|
473
|
+
|
|
474
|
+
@param maid Maid
|
|
475
|
+
@param child Instance
|
|
476
|
+
@param copy Instance
|
|
477
|
+
]]
|
|
478
|
+
function Replicator:_setupObjectValueReplication(maid, child, copy)
|
|
479
|
+
assert(Maid.isMaid(maid), "Bad maid")
|
|
480
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
481
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
482
|
+
|
|
483
|
+
local symbol = newproxy(true)
|
|
484
|
+
|
|
485
|
+
maid:GiveTask(child:GetPropertyChangedSignal("Value"):Connect(function()
|
|
486
|
+
maid[symbol] = self:_doObjectValueReplication(child, copy)
|
|
487
|
+
end))
|
|
488
|
+
maid[symbol] = self:_doObjectValueReplication(child, copy)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
function Replicator:_doObjectValueReplication(child, copy)
|
|
492
|
+
assert(typeof(child) == "Instance", "Bad child")
|
|
493
|
+
assert(typeof(copy) == "Instance", "Bad copy")
|
|
494
|
+
|
|
495
|
+
local childValue = child.Value
|
|
496
|
+
if childValue then
|
|
497
|
+
local maid = Maid.new()
|
|
498
|
+
|
|
499
|
+
maid:GiveTask(self._references:ObserveReferenceChanged(childValue,
|
|
500
|
+
function(newValue)
|
|
501
|
+
if newValue then
|
|
502
|
+
copy.Value = newValue
|
|
503
|
+
else
|
|
504
|
+
-- Fall back to original value (pointing outside of tree)
|
|
505
|
+
newValue = childValue
|
|
506
|
+
end
|
|
507
|
+
end))
|
|
508
|
+
|
|
509
|
+
return maid
|
|
510
|
+
else
|
|
511
|
+
copy.Value = nil
|
|
512
|
+
return nil
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
--[[
|
|
517
|
+
Computes folder replication type based upon the folder name
|
|
518
|
+
and inherited folder replication type.
|
|
519
|
+
|
|
520
|
+
@param childName string
|
|
521
|
+
]]
|
|
522
|
+
function Replicator:_getFolderReplicationType(childName)
|
|
523
|
+
assert(type(childName) == "string", "Bad childName")
|
|
524
|
+
|
|
525
|
+
return ReplicationTypeUtils.getFolderReplicationType(
|
|
526
|
+
childName,
|
|
527
|
+
self._replicationType.Value)
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
function Replicator:_setupAttributeReplication(maid, child, copy)
|
|
531
|
+
for key, value in pairs(child:GetAttributes()) do
|
|
532
|
+
child:SetAttribute(key, value)
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
maid:GiveTask(child.AttributeChanged:Connect(function(attribute)
|
|
536
|
+
copy:SetAttribute(attribute, child:GetAttribute(attribute))
|
|
537
|
+
end))
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
--[=[
|
|
541
|
+
Cleans up the replicator disconnecting all events and cleaning up
|
|
542
|
+
created instances.
|
|
543
|
+
]=]
|
|
544
|
+
function Replicator:Destroy()
|
|
545
|
+
self._maid:DoCleaning()
|
|
546
|
+
setmetatable(self, nil)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
return Replicator
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
Handles mapping of references to the new value.
|
|
3
|
+
|
|
4
|
+
@class ReplicatorReferences
|
|
5
|
+
]=]
|
|
6
|
+
|
|
7
|
+
local ReplicatorReferences = {}
|
|
8
|
+
ReplicatorReferences.ClassName = "ReplicatorReferences"
|
|
9
|
+
ReplicatorReferences.__index = ReplicatorReferences
|
|
10
|
+
|
|
11
|
+
function ReplicatorReferences.new()
|
|
12
|
+
local self = setmetatable({}, ReplicatorReferences)
|
|
13
|
+
|
|
14
|
+
self._lookup = {}
|
|
15
|
+
self._listeners = {} --[orig] = { callback }
|
|
16
|
+
|
|
17
|
+
return self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
--[=[
|
|
21
|
+
Returns true if the argument is a replicator references
|
|
22
|
+
|
|
23
|
+
@param replicatorReferences any?
|
|
24
|
+
@return boolean
|
|
25
|
+
]=]
|
|
26
|
+
function ReplicatorReferences.isReplicatorReferences(replicatorReferences)
|
|
27
|
+
return type(replicatorReferences) == "table" and
|
|
28
|
+
getmetatable(replicatorReferences) == ReplicatorReferences
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
function ReplicatorReferences:SetReference(orig, replicated)
|
|
32
|
+
assert(typeof(orig) == "Instance", "Bad orig")
|
|
33
|
+
assert(typeof(replicated) == "Instance", "Bad replicated")
|
|
34
|
+
|
|
35
|
+
if self._lookup[orig] ~= replicated then
|
|
36
|
+
self._lookup[orig] = replicated
|
|
37
|
+
self:_fireSubs(orig, replicated)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
function ReplicatorReferences:UnsetReference(orig, replicated)
|
|
42
|
+
assert(typeof(orig) == "Instance", "Bad orig")
|
|
43
|
+
assert(typeof(replicated) == "Instance", "Bad replicated")
|
|
44
|
+
|
|
45
|
+
if self._lookup[orig] == replicated then
|
|
46
|
+
self._lookup[orig] = nil
|
|
47
|
+
self:_fireSubs(orig, nil)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
function ReplicatorReferences:_fireSubs(orig, newValue)
|
|
52
|
+
assert(typeof(orig) == "Instance", "Bad orig")
|
|
53
|
+
|
|
54
|
+
local listeners = self._listeners[orig]
|
|
55
|
+
if not listeners then
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
for _, callback in pairs(listeners) do
|
|
60
|
+
task.spawn(callback, newValue)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
--[=[
|
|
65
|
+
Observes when a reference changes. Discount Rx observable since we're
|
|
66
|
+
the loader and don't want a whole copy of Rx.
|
|
67
|
+
|
|
68
|
+
@param orig Instance
|
|
69
|
+
@param callback function
|
|
70
|
+
@return function -- Call to disconnect
|
|
71
|
+
]=]
|
|
72
|
+
function ReplicatorReferences:ObserveReferenceChanged(orig, callback)
|
|
73
|
+
assert(typeof(orig) == "Instance", "Bad orig")
|
|
74
|
+
assert(type(callback) == "function", "Bad callback")
|
|
75
|
+
|
|
76
|
+
-- register
|
|
77
|
+
do
|
|
78
|
+
local listeners = self._listeners[orig]
|
|
79
|
+
if not listeners then
|
|
80
|
+
listeners = {}
|
|
81
|
+
self._listeners[orig] = listeners
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
table.insert(listeners, callback)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
task.spawn(callback, self._lookup[orig])
|
|
88
|
+
|
|
89
|
+
-- Unregister
|
|
90
|
+
return function()
|
|
91
|
+
local listeners = self._listeners[orig]
|
|
92
|
+
if not listeners then
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
local index = table.find(listeners, callback)
|
|
97
|
+
if index then
|
|
98
|
+
table.remove(listeners, index)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
return ReplicatorReferences
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
@class ReplicatorUtils
|
|
3
|
+
]=]
|
|
4
|
+
|
|
5
|
+
local ReplicatorUtils = {}
|
|
6
|
+
|
|
7
|
+
function ReplicatorUtils.cloneWithoutChildren(value)
|
|
8
|
+
local original = {}
|
|
9
|
+
for _, item in pairs(value:GetChildren()) do
|
|
10
|
+
if item.Archivable then
|
|
11
|
+
original[item] = true
|
|
12
|
+
item.Archivable = false
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
local copy = value:Clone()
|
|
16
|
+
|
|
17
|
+
for item, _ in pairs(original) do
|
|
18
|
+
item.Archivable = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return copy
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return ReplicatorUtils
|