Skip to content

Commit e013f60

Browse files
committed
fix removing items from SetSchema and CollectionSchema
1 parent a24f974 commit e013f60

File tree

6 files changed

+74
-10
lines changed

6 files changed

+74
-10
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@colyseus/schema",
3-
"version": "3.0.19",
3+
"version": "3.0.20",
44
"description": "Binary state serializer with delta encoding for games",
55
"bin": {
66
"schema-codegen": "./bin/schema-codegen",

src/encoder/Encoder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class Encoder<T extends Schema = any> {
6464
view.invisible.add(changeTree);
6565
continue; // skip this change tree
6666

67-
} else if (view.invisible.has(changeTree)) {
67+
} else {
6868
view.invisible.delete(changeTree); // remove from invisible list
6969
}
7070
}

src/types/custom/CollectionSchema.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex } from "../symbols";
1+
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd } from "../symbols";
22
import { ChangeTree } from "../../encoder/ChangeTree";
33
import { OPERATION } from "../../encoding/spec";
44
import { registerType } from "../registry";
@@ -10,8 +10,10 @@ import type { StateView } from "../../encoder/StateView";
1010
type K = number; // TODO: allow to specify K generic on MapSchema.
1111

1212
export class CollectionSchema<V=any> implements Collection<K, V>{
13+
1314
protected $items: Map<number, V> = new Map<number, V>();
1415
protected $indexes: Map<number, number> = new Map<number, number>();
16+
protected deletedItems: { [field: string]: V } = {};
1517

1618
protected $refId: number = 0;
1719

@@ -31,7 +33,7 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
3133
return (
3234
!view ||
3335
typeof (ref[$childType]) === "string" ||
34-
view.items.has(ref[$getByIndex](index)[$changes])
36+
view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
3537
);
3638
}
3739

@@ -101,7 +103,7 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
101103
return false;
102104
}
103105

104-
this[$changes].delete(index);
106+
this.deletedItems[index] = this[$changes].delete(index);
105107
this.$indexes.delete(index);
106108

107109
return this.$items.delete(index);
@@ -162,6 +164,10 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
162164
this.$indexes.delete(index);
163165
}
164166

167+
protected [$onEncodeEnd]() {
168+
this.deletedItems = {};
169+
}
170+
165171
toArray() {
166172
return Array.from(this.$items.values());
167173
}

src/types/custom/MapSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, C
143143
delete(key: K) {
144144
const index = this[$changes].indexes[key];
145145

146-
this.deletedItems[index] = this[$changes].delete(index);;
146+
this.deletedItems[index] = this[$changes].delete(index);
147147

148148
return this.$items.delete(key);
149149
}

src/types/custom/SetSchema.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { OPERATION } from "../../encoding/spec";
22
import { registerType } from "../registry";
3-
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex } from "../symbols";
3+
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd } from "../symbols";
44
import { Collection } from "../HelperTypes";
55
import { ChangeTree } from "../../encoder/ChangeTree";
66
import { encodeKeyValueOperation } from "../../encoder/EncodeOperation";
@@ -11,6 +11,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
1111

1212
protected $items: Map<number, V> = new Map<number, V>();
1313
protected $indexes: Map<number, number> = new Map<number, number>();
14+
protected deletedItems: { [field: string]: V } = {};
1415

1516
protected $refId: number = 0;
1617

@@ -30,7 +31,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
3031
return (
3132
!view ||
3233
typeof (ref[$childType]) === "string" ||
33-
view.items.has(ref[$getByIndex](index)[$changes])
34+
view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
3435
);
3536
}
3637

@@ -98,7 +99,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
9899
return false;
99100
}
100101

101-
this[$changes].delete(index);
102+
this.deletedItems[index] = this[$changes].delete(index);
102103
this.$indexes.delete(index);
103104

104105
return this.$items.delete(index);
@@ -172,6 +173,10 @@ export class SetSchema<V=any> implements Collection<number, V> {
172173
this.$indexes.delete(index);
173174
}
174175

176+
protected [$onEncodeEnd]() {
177+
this.deletedItems = {};
178+
}
179+
175180
toArray() {
176181
return Array.from(this.$items.values());
177182
}

test/StateView.test.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as assert from "assert";
2-
import { Schema, type, view, ArraySchema, MapSchema, StateView, Encoder, ChangeTree, $changes, OPERATION, SetSchema } from "../src";
2+
import { Schema, type, view, ArraySchema, MapSchema, StateView, Encoder, ChangeTree, $changes, OPERATION, SetSchema, CollectionSchema } from "../src";
33
import { createClientWithView, encodeMultiple, assertEncodeAllMultiple, getDecoder, getEncoder, createInstanceFromReflection, encodeAllForView, encodeAllMultiple } from "./Schema";
44
import { getDecoderStateCallbacks } from "../src/decoder/strategy/StateCallbacks";
55
import { nanoid } from "nanoid";
@@ -1060,6 +1060,59 @@ describe("StateView", () => {
10601060
});
10611061
});
10621062

1063+
describe("Deep and nested structures", () => {
1064+
it("@view() on SetSchema with CollectionSchema as child", () => {
1065+
class PointState extends Schema {
1066+
@type("number") x: number = 0
1067+
@type("number") y: number = 0
1068+
}
1069+
class OrbState extends PointState {
1070+
@type("string") id = Math.random().toString(36).substring(7);
1071+
@type("uint8") score: number = 1
1072+
@type("uint32") color: number = 0xff0000
1073+
@type("uint8") statusEffect: number;
1074+
}
1075+
class Zone extends Schema {
1076+
@type({ collection: OrbState }) orbs = new CollectionSchema<OrbState>()
1077+
}
1078+
class Player extends Schema {
1079+
@view() @type({ set: Zone }) loadedZones: SetSchema<Zone> = new SetSchema<Zone>()
1080+
}
1081+
class State extends Schema {
1082+
@type({ map: Player }) players = new MapSchema<Player>();
1083+
}
1084+
1085+
const state = new State();
1086+
const encoder = getEncoder(state);
1087+
1088+
const client1 = createClientWithView(state);
1089+
encodeMultiple(encoder, state, [client1]);
1090+
1091+
const player = new Player();
1092+
state.players.set("one", player);
1093+
1094+
const zones: Zone[] = [];
1095+
for (let i = 0; i < 3; i++) {
1096+
const zone = new Zone();
1097+
zone.orbs.add(new OrbState());
1098+
zone.orbs.add(new OrbState());
1099+
player.loadedZones.add(zone);
1100+
zones.push(zone);
1101+
}
1102+
1103+
client1.view.add(zones[0]);
1104+
encodeMultiple(encoder, state, [client1]);
1105+
assertEncodeAllMultiple(encoder, state, [client1])
1106+
1107+
assert.strictEqual(2, client1.state.players.get('one').loadedZones.toArray()[0].orbs.size);
1108+
1109+
player.loadedZones.delete(zones[0]);
1110+
encodeMultiple(encoder, state, [client1]);
1111+
1112+
assertEncodeAllMultiple(encoder, state, [client1])
1113+
});
1114+
});
1115+
10631116
describe("checkIsFiltered", () => {
10641117
/**
10651118
* prepare a schema with a filtered property

0 commit comments

Comments
 (0)