Skip to content

Commit edab190

Browse files
committed
@colyseus/schema: improve instance sharing and ref count tracking.
1 parent 98f5190 commit edab190

File tree

3 files changed

+48
-39
lines changed

3 files changed

+48
-39
lines changed

colyseus/serializer/schema/decoder.lua

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ function Decoder:new(state, context)
203203

204204
-- set root state refid
205205
state.__refid = 1
206-
instance.refs:set(state.__refid, state)
206+
instance.refs:add(state.__refid, state, true)
207207

208208
return instance
209209
end
@@ -279,15 +279,15 @@ function Decoder:decode_value(decoder, operation, ref, field_index, field_type,
279279
-- DELETE operations
280280
--
281281
if bit.band(operation, OPERATION.DELETE) == OPERATION.DELETE then
282-
if operation ~= OPERATION.DELETE_AND_ADD then
283-
ref:delete_by_index(field_index)
284-
end
285-
286282
-- Flag `ref_id` for garbage collection.
287283
if type(previous_value) == "table" and previous_value.__refid ~= nil then
288284
decoder.refs:remove(previous_value.__refid)
289285
end
290286

287+
if operation ~= OPERATION.DELETE_AND_ADD then
288+
ref:delete_by_index(field_index)
289+
end
290+
291291
value = nil
292292
end
293293

@@ -302,26 +302,14 @@ function Decoder:decode_value(decoder, operation, ref, field_index, field_type,
302302
local __refid = decode.number(bytes, it) + 1
303303
value = decoder.refs:get(__refid)
304304

305-
if previous_value ~= nil then
306-
local previous_refid = previous_value.__refid
307-
if (
308-
previous_refid > 0 and
309-
previous_refid ~= __refid and
310-
-- FIXME: we may need to check for REPLACE operation as well
311-
bit.band(operation, OPERATION.DELETE) == OPERATION.DELETE
312-
) then
313-
decoder.refs:remove(previous_refid)
314-
end
315-
end
316-
317305
if bit.band(operation, OPERATION.ADD) == OPERATION.ADD then
318306
local concrete_child_type = decoder:get_schema_type(bytes, it, field_type);
319307
if value == nil then
320308
value = concrete_child_type:new()
321309
value.__refid = __refid
322310
end
323311

324-
decoder.refs:set(__refid, value, (
312+
decoder.refs:add(__refid, value, (
325313
value ~= previous_value or
326314
(operation == OPERATION.DELETE_AND_ADD and value == previous_value)
327315
));
@@ -348,12 +336,15 @@ function Decoder:decode_value(decoder, operation, ref, field_index, field_type,
348336

349337
if previous_value ~= nil then
350338
if previous_value.__refid ~= nil and previous_value.__refid ~= __refid then
351-
decoder.refs:remove(previous_value.__refid)
352339

353340
--
354341
-- Trigger on_remove() if structure has been replaced.
355342
--
356343
previous_value:each(function(val, key)
344+
if type(val) == "table" and val.__refid ~= nil then
345+
decoder.refs:remove(val.__refid)
346+
end
347+
357348
table.insert(all_changes, {
358349
__refid = previous_value.__refid,
359350
op = OPERATION.DELETE,
@@ -365,7 +356,10 @@ function Decoder:decode_value(decoder, operation, ref, field_index, field_type,
365356
end
366357
end
367358

368-
decoder.refs:set(__refid, value, (value_ref ~= previous_value))
359+
decoder.refs:add(__refid, value, (
360+
value_ref ~= previous_value or
361+
(operation == OPERATION.DELETE_AND_ADD and value_ref == previous_value)
362+
))
369363
end
370364

371365
return value, previous_value

colyseus/serializer/schema/reference_tracker.lua

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ function reference_tracker:get(ref_id)
2626
return self.refs[ref_id]
2727
end
2828

29-
function reference_tracker:set(ref_id, ref, increment_count)
29+
function reference_tracker:add(ref_id, ref, increment_count)
3030
self.refs[ref_id] = ref
3131

32-
if increment_count == nil or increment_count == true then
32+
if increment_count == true then
3333
self.ref_counts[ref_id] = (self.ref_counts[ref_id] or 0) + 1
3434
end
3535

@@ -39,12 +39,27 @@ function reference_tracker:set(ref_id, ref, increment_count)
3939
end
4040

4141
function reference_tracker:remove(ref_id)
42-
self.ref_counts[ref_id] = self.ref_counts[ref_id] - 1;
42+
local ref_count = self.ref_counts[ref_id]
4343

44-
local added_to_deleted_refs = (self.deleted_refs[ref_id] == nil)
45-
self.deleted_refs[ref_id] = true
44+
-- skip if ref_id is not being tracked
45+
if ref_count == nil then
46+
print("trying to remove ref_id that doesn't exist: " .. tostring(ref_id))
47+
return false
48+
end
49+
50+
if ref_count == 0 then
51+
print("trying to remove ref_id with 0 ref_count: " .. tostring(ref_id))
52+
return false
53+
end
54+
55+
self.ref_counts[ref_id] = ref_count - 1;
56+
57+
if self.ref_counts[ref_id] <= 0 then
58+
self.deleted_refs[ref_id] = true
59+
return true
60+
end
4661

47-
return added_to_deleted_refs
62+
return false
4863
end
4964

5065
function reference_tracker:count()
@@ -72,20 +87,20 @@ function reference_tracker:garbage_collection()
7287
--
7388
if ref._schema ~= nil then
7489
for field, field_type in pairs(ref._schema) do
75-
if (
76-
type(field_type) ~= "string" and
77-
ref[field] ~= nil and
78-
ref[field].__refid ~= nil and
79-
self:remove(ref[field].__refid)
80-
) then
81-
table.insert(deleted_refs, ref[field].__refid)
90+
local child_ref_id = type(field_type) ~= "string" and ref[field] ~= nil and ref[field].__refid
91+
if (child_ref_id ~= nil and self.deleted_refs[child_ref_id] == nil and self:remove(child_ref_id)) then
92+
table.insert(deleted_refs, child_ref_id)
8293
end
8394
end
8495

8596
elseif ref._child_type['new'] ~= nil then
8697
ref:each(function(value)
87-
if self:remove(value.__refid) then
88-
table.insert(deleted_refs, value.__refid)
98+
local child_ref_id = value.__refid
99+
if (
100+
self.deleted_refs[child_ref_id] == nil and
101+
self:remove(child_ref_id)
102+
) then
103+
table.insert(deleted_refs, child_ref_id)
89104
end
90105
end)
91106
end

test/schema_serializer.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,18 +355,18 @@ return function()
355355
local state = InstanceSharingTypes:new()
356356
local decoder = Decoder:new(state)
357357

358-
decoder:decode({ 130, 1, 131, 2, 128, 3, 129, 3, 255, 1, 255, 2, 255, 3, 128, 4, 255, 3, 128, 4, 255, 4, 128, 10, 129, 10, 255, 4, 128, 10, 129, 10 });
358+
decoder:decode({ 130, 1, 131, 2, 128, 3, 129, 3, 255, 3, 128, 4, 255, 4, 128, 10, 129, 10 });
359359
assert_equal(state.player1, state.player2);
360360
assert_equal(state.player1.position, state.player2.position);
361361
assert_equal(decoder.refs.ref_counts[state.player1.__refid], 2);
362362
assert_equal(5, decoder.refs:count());
363363

364-
decoder:decode({ 130, 1, 131, 2, 64, 65 });
364+
decoder:decode({ 64, 65, 255, 0, 64, 65 });
365365
assert_equal(nil, state.player1);
366366
assert_equal(nil, state.player2);
367367
assert_equal(3, decoder.refs:count());
368368

369-
decoder:decode({ 255, 1, 128, 0, 5, 128, 1, 5, 128, 2, 5, 128, 3, 6, 255, 5, 128, 7, 255, 6, 128, 8, 255, 7, 128, 10, 129, 10, 255, 8, 128, 10, 129, 10 });
369+
decoder:decode({ 255, 1, 128, 0, 5, 128, 1, 5, 128, 2, 5, 128, 3, 7, 255, 5, 128, 6, 255, 6, 128, 10, 129, 10, 255, 7, 128, 8, 255, 8, 128, 10, 129, 10 });
370370
assert_equal(state.arrayOfPlayers[1], state.arrayOfPlayers[2]);
371371
assert_equal(state.arrayOfPlayers[2], state.arrayOfPlayers[3]);
372372
assert_not_equal(state.arrayOfPlayers[3], state.arrayOfPlayers[4]);
@@ -378,7 +378,7 @@ return function()
378378
local previous_array_schema__refid = state.arrayOfPlayers.__refid;
379379

380380
-- Replacing ArraySchema
381-
decoder:decode({ 130, 9, 255, 9, 128, 0, 10, 255, 10, 128, 11, 255, 11, 128, 10, 129, 20 });
381+
decoder:decode({ 194, 9, 255, 9, 128, 0, 10, 255, 10, 128, 11, 255, 11, 128, 10, 129, 20 });
382382
assert_equal(false, decoder.refs:has(previous_array_schema__refid));
383383
assert_equal(1, state.arrayOfPlayers:length());
384384
assert_equal(5, decoder.refs:count());

0 commit comments

Comments
 (0)