graphos-world-client 1.0.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.
@@ -0,0 +1,34 @@
1
+ import type { IObject, ISendEvent } from "./types.js";
2
+ export declare class SpawnEvent extends Event {
3
+ readonly object: IObject;
4
+ constructor(object: IObject);
5
+ }
6
+ export declare class DespawnEvent extends Event {
7
+ readonly object: IObject;
8
+ constructor(object: IObject);
9
+ }
10
+ export declare class ChangeEvent extends Event {
11
+ readonly object: IObject;
12
+ readonly changes: Partial<IObject>;
13
+ constructor(object: IObject, changes: Partial<IObject>);
14
+ }
15
+ type ObjectIterator<T> = IterableIterator<T>;
16
+ export declare function getDeviceId(): string;
17
+ export declare class Client extends EventTarget {
18
+ readonly url: string;
19
+ private _window;
20
+ private _socket;
21
+ private _isConnected;
22
+ private _objects;
23
+ constructor(url?: string);
24
+ private onSocketConnect;
25
+ private onWorldEventRecordClear;
26
+ protected onDataBindingChange(object: IObject, v: Partial<IObject>): void;
27
+ private onWorldEventRecord;
28
+ close(): void;
29
+ sendEvent(event: ISendEvent): void;
30
+ objects(): ObjectIterator<IObject>;
31
+ getObjectsByTable(table: string): ObjectIterator<IObject>;
32
+ getObjectByTable(table: string): IObject | null;
33
+ }
34
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,204 @@
1
+ export class SpawnEvent extends Event {
2
+ object;
3
+ constructor(object) {
4
+ super("spawn");
5
+ this.object = object;
6
+ }
7
+ }
8
+ export class DespawnEvent extends Event {
9
+ object;
10
+ constructor(object) {
11
+ super("despawn");
12
+ this.object = object;
13
+ }
14
+ }
15
+ export class ChangeEvent extends Event {
16
+ object;
17
+ changes;
18
+ constructor(object, changes) {
19
+ super("change");
20
+ this.object = object;
21
+ this.changes = changes;
22
+ }
23
+ }
24
+ export function getDeviceId() {
25
+ let id = localStorage.getItem('device-id');
26
+ if (!id) {
27
+ id = crypto.randomUUID();
28
+ localStorage.setItem('device-id', id);
29
+ }
30
+ return id;
31
+ }
32
+ export class Client extends EventTarget {
33
+ url;
34
+ _window = null;
35
+ _socket = null;
36
+ _isConnected = false;
37
+ _objects = new Map();
38
+ constructor(url = "ws://127.0.0.1:3411/ws") {
39
+ super();
40
+ this.url = url;
41
+ let w = window;
42
+ while (w.GRAPHOS_WORLD_PLUGIN_CLIENT_VERSION === undefined) {
43
+ if (w.parent && w.parent !== w) {
44
+ w = w.parent;
45
+ }
46
+ else {
47
+ w = null;
48
+ break;
49
+ }
50
+ }
51
+ this._window = w;
52
+ if (w) {
53
+ w.addEventListener('world-event-record-clear', this.onWorldEventRecordClear.bind(this));
54
+ w.addEventListener('world-event-record', this.onWorldEventRecord.bind(this));
55
+ }
56
+ else {
57
+ this.onSocketConnect();
58
+ }
59
+ }
60
+ onSocketConnect() {
61
+ if (this._socket) {
62
+ this._socket.close();
63
+ this._socket = null;
64
+ }
65
+ this._socket = new WebSocket(this.url);
66
+ console.log('Connecting to WebSocket...');
67
+ this._socket.onmessage = (e) => {
68
+ let envelope = null;
69
+ try {
70
+ envelope = JSON.parse(e.data);
71
+ }
72
+ catch {
73
+ envelope = null;
74
+ }
75
+ if (!envelope)
76
+ return;
77
+ if (envelope.type === 'world-event-record') {
78
+ let record = envelope.payload;
79
+ this.onWorldEventRecord(new CustomEvent('world-event-record', { detail: record }));
80
+ }
81
+ else if (envelope.type === 'world-event-record-clear') {
82
+ this.onWorldEventRecordClear();
83
+ }
84
+ };
85
+ this._socket.onopen = () => {
86
+ console.log('WebSocket connected');
87
+ this._isConnected = true;
88
+ let envelope = {
89
+ type: 'world-device',
90
+ payload: {
91
+ id: getDeviceId(),
92
+ type: 'cocos',
93
+ platform: "web",
94
+ language: navigator.language,
95
+ os: navigator.platform,
96
+ osVersion: navigator.userAgent,
97
+ },
98
+ };
99
+ this._socket.send(JSON.stringify(envelope));
100
+ console.log('WebSocket connected');
101
+ };
102
+ this._socket.onclose = () => {
103
+ this._isConnected = false;
104
+ console.log('WebSocket disconnected');
105
+ setTimeout(() => { this.onSocketConnect(); }, 3000);
106
+ };
107
+ }
108
+ onWorldEventRecordClear() {
109
+ this._objects.clear();
110
+ }
111
+ onDataBindingChange(object, v) {
112
+ }
113
+ onWorldEventRecord(e) {
114
+ let record = e.detail;
115
+ if (record.type === 'get' || record.type === 'add') {
116
+ let v = record.data;
117
+ let vs = this._objects.get(v.table);
118
+ if (!vs) {
119
+ vs = new Map();
120
+ this._objects.set(v.table, vs);
121
+ }
122
+ let old = vs.get(v.id);
123
+ if (old) {
124
+ this.dispatchEvent(new DespawnEvent(old));
125
+ }
126
+ vs.set(v.id, v);
127
+ this.dispatchEvent(new SpawnEvent(v));
128
+ }
129
+ else if (record.type === 'set') {
130
+ let v = record.data;
131
+ let vs = this._objects.get(v.table);
132
+ if (!vs)
133
+ return;
134
+ let old = vs.get(v.id);
135
+ if (!old)
136
+ return;
137
+ Object.assign(old, v);
138
+ this.dispatchEvent(new ChangeEvent(old, v));
139
+ }
140
+ else if (record.type === 'del') {
141
+ let v = record.data;
142
+ let vs = this._objects.get(v.table);
143
+ if (!vs)
144
+ return;
145
+ let old = vs.get(v.id);
146
+ if (!old)
147
+ return;
148
+ vs.delete(v.id);
149
+ this.dispatchEvent(new DespawnEvent(old));
150
+ }
151
+ }
152
+ close() {
153
+ if (this._window) {
154
+ this._window.removeEventListener('world-event-record-clear', this.onWorldEventRecordClear.bind(this));
155
+ this._window.removeEventListener('world-event-record', this.onWorldEventRecord.bind(this));
156
+ }
157
+ else if (this._socket) {
158
+ this._socket.close();
159
+ this._socket = null;
160
+ }
161
+ }
162
+ sendEvent(event) {
163
+ if (this._window) {
164
+ this._window.dispatchEvent(new CustomEvent('world-send-event', { detail: event }));
165
+ }
166
+ else if (this._isConnected && this._socket) {
167
+ let envelope = {
168
+ type: 'world-send-event',
169
+ payload: event,
170
+ };
171
+ this._socket.send(JSON.stringify(envelope));
172
+ }
173
+ }
174
+ objects() {
175
+ const self = this;
176
+ return (function* () {
177
+ for (const tableObjects of self._objects.values()) {
178
+ for (const object of tableObjects.values()) {
179
+ yield object;
180
+ }
181
+ }
182
+ })();
183
+ }
184
+ getObjectsByTable(table) {
185
+ const self = this;
186
+ return (function* () {
187
+ const tableObjects = self._objects.get(table);
188
+ if (!tableObjects)
189
+ return;
190
+ for (const object of tableObjects.values()) {
191
+ yield object;
192
+ }
193
+ })();
194
+ }
195
+ getObjectByTable(table) {
196
+ const tableObjects = this._objects.get(table);
197
+ if (!tableObjects)
198
+ return null;
199
+ for (const object of tableObjects.values()) {
200
+ return object;
201
+ }
202
+ return null;
203
+ }
204
+ }
@@ -0,0 +1,27 @@
1
+ export interface IEventSource<T extends IObject> {
2
+ readonly object: T;
3
+ readonly id: string;
4
+ readonly table: string;
5
+ }
6
+ export interface IEvent<T extends IObject> {
7
+ readonly type: string;
8
+ readonly source: IEventSource<T>;
9
+ }
10
+ export interface ITable {
11
+ id: string;
12
+ name: string;
13
+ keys: string[];
14
+ }
15
+ export interface IObject {
16
+ table: string;
17
+ id: string;
18
+ [key: string]: any;
19
+ }
20
+ export interface ISendEvent {
21
+ readonly type: string;
22
+ [key: string]: any;
23
+ }
24
+ export interface Vec2 {
25
+ x: number;
26
+ y: number;
27
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "graphos-world-client",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "index.js",
9
+ "scripts": {
10
+ "build": "tsc"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^25.8.0",
14
+ "typescript": "^6.0.3"
15
+ },
16
+ "files": [
17
+ "dist/"
18
+ ]
19
+ }