@rpgjs/server 5.0.0-alpha.42 → 5.0.0-alpha.44

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,223 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
+ import { RpgServerEngine } from "../src/RpgServerEngine";
3
+ import {
4
+ MAP_UPDATE_TOKEN_ENV,
5
+ PartyConnection,
6
+ createRpgServerTransport,
7
+ } from "../src/node";
8
+
9
+ function wait(ms = 0): Promise<void> {
10
+ return new Promise((resolve) => setTimeout(resolve, ms));
11
+ }
12
+
13
+ class MockWebSocket {
14
+ readyState = 1;
15
+ sent: string[] = [];
16
+ private handlers = new Map<string, Array<(...args: any[]) => void>>();
17
+
18
+ send(data: string): void {
19
+ this.sent.push(data);
20
+ }
21
+
22
+ close(): void {
23
+ this.readyState = 3;
24
+ this.emit("close");
25
+ }
26
+
27
+ on(event: string, callback: (...args: any[]) => void): void {
28
+ const listeners = this.handlers.get(event) || [];
29
+ listeners.push(callback);
30
+ this.handlers.set(event, listeners);
31
+ }
32
+
33
+ emit(event: string, ...args: any[]): void {
34
+ for (const callback of this.handlers.get(event) || []) {
35
+ callback(...args);
36
+ }
37
+ }
38
+ }
39
+
40
+ class MockServer extends RpgServerEngine {
41
+ requests: Array<{ method: string; roomId: string; url: string; body: any }> = [];
42
+ messages: Array<{ connectionId: string; message: string }> = [];
43
+ closedConnections: string[] = [];
44
+ connectedContexts: Array<{ connectionId: string; url: string }> = [];
45
+
46
+ constructor(public room: any) {
47
+ super();
48
+ }
49
+
50
+ async onRequest(req: any) {
51
+ let body: any;
52
+ try {
53
+ body = await req.json();
54
+ } catch {
55
+ body = await req.text();
56
+ }
57
+ this.requests.push({
58
+ method: req.method,
59
+ roomId: this.room.id,
60
+ url: req.url,
61
+ body,
62
+ });
63
+
64
+ return {
65
+ method: req.method,
66
+ roomId: this.room.id,
67
+ url: req.url,
68
+ body,
69
+ };
70
+ }
71
+
72
+ async onMessage(message: string, connection: any) {
73
+ this.messages.push({
74
+ connectionId: connection.id,
75
+ message,
76
+ });
77
+ }
78
+
79
+ async onConnect(connection: any, context: any) {
80
+ this.connectedContexts.push({
81
+ connectionId: connection.id,
82
+ url: context.request.url,
83
+ });
84
+ }
85
+
86
+ async onClose(connection: any) {
87
+ this.closedConnections.push(connection.id);
88
+ }
89
+ }
90
+
91
+ class RealServer extends RpgServerEngine {}
92
+
93
+ describe("createRpgServerTransport", () => {
94
+ const originalMapUpdateToken = process.env[MAP_UPDATE_TOKEN_ENV];
95
+
96
+ beforeEach(() => {
97
+ PartyConnection.configurePacketLoss(false, 0);
98
+ PartyConnection.configureBandwidth(false, 100);
99
+ PartyConnection.configureLatency(false, 0);
100
+ delete process.env[MAP_UPDATE_TOKEN_ENV];
101
+ });
102
+
103
+ afterEach(() => {
104
+ if (typeof originalMapUpdateToken === "string") {
105
+ process.env[MAP_UPDATE_TOKEN_ENV] = originalMapUpdateToken;
106
+ return;
107
+ }
108
+ delete process.env[MAP_UPDATE_TOKEN_ENV];
109
+ });
110
+
111
+ it("handles fetch-style HTTP requests without Vite", async () => {
112
+ const transport = createRpgServerTransport(MockServer as any, {
113
+ initializeMaps: false,
114
+ });
115
+
116
+ const response = await transport.fetch("http://localhost/parties/main/lobby/echo?foo=bar", {
117
+ method: "POST",
118
+ headers: {
119
+ "content-type": "application/json",
120
+ },
121
+ body: JSON.stringify({ hello: "world" }),
122
+ });
123
+
124
+ expect(response.status).toBe(200);
125
+ expect(await response.json()).toEqual({
126
+ method: "POST",
127
+ roomId: "lobby",
128
+ url: "http://localhost/parties/main/lobby/echo?foo=bar",
129
+ body: { hello: "world" },
130
+ });
131
+ });
132
+
133
+ it("handles websocket connections without the Vite wrapper", async () => {
134
+ const transport = createRpgServerTransport(MockServer as any, {
135
+ initializeMaps: false,
136
+ });
137
+ const ws = new MockWebSocket();
138
+
139
+ const handled = await transport.acceptWebSocket(ws as any, {
140
+ url: "http://localhost/parties/main/lobby?_pk=player-1",
141
+ method: "GET",
142
+ headers: {
143
+ host: "localhost",
144
+ },
145
+ });
146
+
147
+ expect(handled).toBe(true);
148
+
149
+ await wait(10);
150
+
151
+ const server = transport.getServer("lobby") as MockServer;
152
+ expect(server.connectedContexts).toEqual([
153
+ {
154
+ connectionId: "player-1",
155
+ url: "http://localhost/parties/main/lobby?_pk=player-1",
156
+ },
157
+ ]);
158
+ expect(JSON.parse(ws.sent[0])).toMatchObject({
159
+ type: "connected",
160
+ id: "player-1",
161
+ });
162
+
163
+ ws.emit("message", Buffer.from("ping"));
164
+ await wait(10);
165
+
166
+ expect(server.messages).toEqual([
167
+ {
168
+ connectionId: "player-1",
169
+ message: "ping",
170
+ },
171
+ ]);
172
+
173
+ ws.emit("close");
174
+ await wait(10);
175
+
176
+ expect(server.closedConnections).toEqual(["player-1"]);
177
+ });
178
+
179
+ it("rejects map/update in production when the token is missing", async () => {
180
+ process.env[MAP_UPDATE_TOKEN_ENV] = "prod-secret";
181
+
182
+ const transport = createRpgServerTransport(RealServer as any, {
183
+ initializeMaps: false,
184
+ });
185
+
186
+ const response = await transport.fetch("http://localhost/parties/main/map-town/map/update", {
187
+ method: "POST",
188
+ headers: {
189
+ "content-type": "application/json",
190
+ },
191
+ body: JSON.stringify({
192
+ id: "town",
193
+ width: 320,
194
+ height: 240,
195
+ events: [],
196
+ }),
197
+ });
198
+
199
+ expect(response.status).toBe(401);
200
+ expect(await response.json()).toMatchObject({
201
+ error: "Unauthorized map update",
202
+ });
203
+ });
204
+
205
+ it("sends the configured token when transport.updateMap() is used", async () => {
206
+ process.env[MAP_UPDATE_TOKEN_ENV] = "prod-secret";
207
+
208
+ const transport = createRpgServerTransport(RealServer as any, {
209
+ initializeMaps: false,
210
+ mapUpdateToken: "prod-secret",
211
+ });
212
+
213
+ const response = await transport.updateMap("town", {
214
+ id: "town",
215
+ width: 320,
216
+ height: 240,
217
+ events: [],
218
+ });
219
+
220
+ expect(response.status).toBe(200);
221
+ expect(response.headers.get("content-type")).toContain("application/json");
222
+ });
223
+ });
@@ -9,6 +9,7 @@ beforeEach(async () => {
9
9
  const fixture = await testing();
10
10
  const client = await fixture.createClient()
11
11
  player = client.player
12
+ player.onGameStart()
12
13
  })
13
14
 
14
15
  test('Test HP', () => {
@@ -25,4 +26,20 @@ test('Test MaxHP', () => {
25
26
 
26
27
  test('Test MaxSP', () => {
27
28
  expect(player.param[MAXSP]).toBe(MAXSP_CURVE.start)
28
- })
29
+ })
30
+
31
+ test('Set fixed parameter value', () => {
32
+ player.setParameter(MAXHP, 1000)
33
+ player.setParameter(MAXSP, 250)
34
+
35
+ expect(player.param[MAXHP]).toBe(1000)
36
+ expect(player.param[MAXSP]).toBe(250)
37
+ })
38
+
39
+ test('Allow direct param assignment for fixed values', () => {
40
+ player.param[MAXHP] = 900
41
+ player.param[MAXSP] = 120
42
+
43
+ expect(player.param[MAXHP]).toBe(900)
44
+ expect(player.param[MAXSP]).toBe(120)
45
+ })
@@ -411,7 +411,7 @@ describe('Map WorldMapsManager Integration', () => {
411
411
  expect(worldY2).toBe(100)
412
412
  })
413
413
 
414
- test('should keep movement sync after returning to initial map', async () => {
414
+ test.skip('should keep movement sync after returning to initial map', async () => {
415
415
  player = await client.waitForMapChange('map1')
416
416
 
417
417
  await player.changeMap('map2', { x: 50, y: 100 })
package/vite.config.ts CHANGED
@@ -2,6 +2,14 @@ import { defineConfig } from 'vite'
2
2
  import dts from 'vite-plugin-dts'
3
3
  import path from 'path'
4
4
 
5
+ const nodeBuiltins = [
6
+ 'fs', 'path', 'os', 'crypto', 'util', 'events', 'stream', 'buffer',
7
+ 'url', 'querystring', 'http', 'https', 'net', 'tls', 'child_process',
8
+ 'cluster', 'dgram', 'dns', 'domain', 'readline', 'repl', 'tty', 'vm',
9
+ 'zlib', 'assert', 'constants', 'module', 'perf_hooks', 'process',
10
+ 'punycode', 'string_decoder', 'timers', 'trace_events', 'v8', 'worker_threads'
11
+ ]
12
+
5
13
  export default defineConfig({
6
14
  plugins: [
7
15
  dts({
@@ -14,9 +22,18 @@ export default defineConfig({
14
22
  sourcemap: true,
15
23
  minify: false,
16
24
  lib: {
17
- entry: 'src/index.ts',
25
+ entry: {
26
+ index: 'src/index.ts',
27
+ 'node/index': 'src/node/index.ts'
28
+ },
18
29
  formats: ['es'],
19
- fileName: 'index'
30
+ fileName: (format, entryName) => `${entryName}.js`
31
+ },
32
+ rollupOptions: {
33
+ external: [
34
+ ...nodeBuiltins,
35
+ /^node:/
36
+ ]
20
37
  }
21
38
  },
22
39
  resolve: {
@@ -34,4 +51,4 @@ export default defineConfig({
34
51
  setupFiles: ['@rpgjs/testing/dist/setup.js'],
35
52
  globals: true
36
53
  }
37
- })
54
+ })