@colyseus/core
Version:
Multiplayer Framework for Node.js.
8 lines (7 loc) • 8.41 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/serializer/SchemaSerializer.ts"],
"sourcesContent": ["import { Serializer } from './Serializer.js';\n\nimport { Encoder, dumpChanges, Reflection, Schema, Iterator, StateView } from '@colyseus/schema';\nimport { debugPatch } from '../Debug.js';\nimport { Protocol } from '../Protocol.js';\nimport { Client, ClientState } from '../Transport.js';\n\nconst SHARED_VIEW = {};\n\nexport class SchemaSerializer<T extends Schema> implements Serializer<T> {\n public id = 'schema';\n\n protected encoder: Encoder<T>;\n protected hasFilters: boolean = false;\n\n protected handshakeCache: Buffer;\n\n // flag to avoid re-encoding full state if no changes were made\n protected needFullEncode: boolean = true;\n\n // TODO: make this optional. allocating a new buffer for each room may not be always necessary.\n protected fullEncodeBuffer: Buffer = Buffer.allocUnsafe(Encoder.BUFFER_SIZE);\n protected fullEncodeCache: Buffer;\n protected sharedOffsetCache: Iterator = { offset: 0 };\n\n protected encodedViews: Map<StateView | typeof SHARED_VIEW, Buffer>;\n\n public reset(newState: T & Schema) {\n this.encoder = new Encoder(newState);\n this.hasFilters = this.encoder.context.hasFilters;\n\n // cache ROOM_STATE byte as part of the encoded buffer\n this.fullEncodeBuffer[0] = Protocol.ROOM_STATE;\n\n if (this.hasFilters) {\n this.encodedViews = new Map();\n }\n }\n\n public getFullState(client?: Client) {\n if (\n this.needFullEncode ||\n this.encoder.root.changes.length > 0 || // TODO: remove this check on 0.17\n // @ts-ignore\n this.encoder.root.changes.next !== undefined // @colyseus/schema 3.0.42+\n ) {\n this.sharedOffsetCache = { offset: 1 };\n this.fullEncodeCache = this.encoder.encodeAll(this.sharedOffsetCache, this.fullEncodeBuffer);\n this.needFullEncode = false;\n }\n\n if (this.hasFilters && client?.view) {\n return this.encoder.encodeAllView(\n client.view,\n this.sharedOffsetCache.offset,\n { ...this.sharedOffsetCache },\n this.fullEncodeBuffer\n );\n\n } else {\n return this.fullEncodeCache;\n }\n }\n\n public applyPatches(clients: Client[]) {\n let numClients = clients.length;\n\n if (numClients === 0) {\n // skip patching and clear changes\n this.encoder.discardChanges();\n return false;\n }\n\n if (!this.encoder.hasChanges) {\n\n // check if views have changes (manual add() or remove() items)\n if (this.hasFilters) {\n //\n // FIXME: refactor this to avoid duplicating code.\n //\n // it's probably better to have 2 different 'applyPatches' methods.\n // (one for handling state with filters, and another for handling state without filters)\n //\n const clientsWithViewChange = clients.filter((client) => {\n return client.state === ClientState.JOINED && client.view?.changes.size > 0\n });\n\n if (clientsWithViewChange.length > 0) {\n const it: Iterator = { offset: 1 };\n\n const sharedOffset = it.offset;\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n clientsWithViewChange.forEach((client) => {\n client.raw(this.encoder.encodeView(client.view, sharedOffset, it));\n });\n }\n }\n\n // skip patching state if:\n // - no clients are connected\n // - no changes were made\n // - no \"filtered changes\" were made when using filters\n return false;\n }\n\n this.needFullEncode = true;\n\n // dump changes for patch debugging\n if (debugPatch.enabled) {\n (debugPatch as any).dumpChanges = dumpChanges(this.encoder.state);\n }\n\n // get patch bytes\n const it: Iterator = { offset: 1 };\n this.encoder.sharedBuffer[0] = Protocol.ROOM_STATE_PATCH;\n\n // encode changes once, for all clients\n const encodedChanges = this.encoder.encode(it);\n\n if (!this.hasFilters) {\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n client.raw(encodedChanges);\n }\n\n } else {\n // cache shared offset\n const sharedOffset = it.offset;\n\n // encode state multiple times, for each client\n while (numClients--) {\n const client = clients[numClients];\n\n //\n // FIXME: avoid this check for each client\n //\n if (client.state !== ClientState.JOINED) {\n continue;\n }\n\n const view = client.view || SHARED_VIEW;\n\n let encodedView = this.encodedViews.get(view);\n\n // allow to pass the same encoded view for multiple clients\n if (encodedView === undefined) {\n encodedView = (view === SHARED_VIEW)\n ? encodedChanges\n : this.encoder.encodeView(client.view, sharedOffset, it);\n this.encodedViews.set(view, encodedView);\n }\n\n client.raw(encodedView);\n }\n\n // clear views\n this.encodedViews.clear();\n }\n\n // discard changes after sending\n this.encoder.discardChanges();\n\n // debug patches\n if (debugPatch.enabled) {\n debugPatch(\n '%d bytes sent to %d clients, %j',\n encodedChanges.length,\n clients.length,\n (debugPatch as any).dumpChanges,\n );\n }\n\n return true;\n }\n\n public handshake() {\n /**\n * Cache handshake to avoid encoding it for each client joining\n */\n if (!this.handshakeCache) {\n //\n // TODO: re-use handshake buffer for all rooms of same type (?)\n //\n this.handshakeCache = (this.encoder.state && Reflection.encode(this.encoder));\n }\n\n return this.handshakeCache;\n }\n\n}\n"],
"mappings": "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",
"names": ["it"]
}