@rpgjs/vite 5.0.0-beta.8 → 5.0.0-beta.9

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/compatibility-v4/flag-transform.d.ts +6 -0
  3. package/dist/compatibility-v4/index.d.ts +22 -0
  4. package/dist/compatibility-v4/load-config-file.d.ts +2 -0
  5. package/dist/compatibility-v4/require-transform.d.ts +2 -0
  6. package/dist/compatibility-v4/utils.d.ts +55 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +1329 -164
  9. package/dist/index.js.map +1 -1
  10. package/dist/rpgjs-plugin.d.ts +1 -0
  11. package/package.json +17 -9
  12. package/src/compatibility-v4/flag-transform.ts +61 -0
  13. package/src/compatibility-v4/index.ts +713 -0
  14. package/src/compatibility-v4/load-config-file.ts +38 -0
  15. package/src/compatibility-v4/require-transform.ts +67 -0
  16. package/src/compatibility-v4/utils.ts +170 -0
  17. package/src/index.ts +2 -1
  18. package/tests/compatibility-v4.spec.ts +105 -0
  19. package/tests/fixtures/v4-game/rpg.toml +11 -0
  20. package/tests/fixtures/v4-game/src/modules/main/characters/assets/hero.svg +2 -0
  21. package/tests/fixtures/v4-game/src/modules/main/characters/hero.ts +2 -0
  22. package/tests/fixtures/v4-game/src/modules/main/client.ts +4 -0
  23. package/tests/fixtures/v4-game/src/modules/main/database/potion.ts +6 -0
  24. package/tests/fixtures/v4-game/src/modules/main/events/npc.ts +4 -0
  25. package/tests/fixtures/v4-game/src/modules/main/gui/menu.vue +2 -0
  26. package/tests/fixtures/v4-game/src/modules/main/maps/map.tmx +2 -0
  27. package/tests/fixtures/v4-game/src/modules/main/maps/map.ts +7 -0
  28. package/tests/fixtures/v4-game/src/modules/main/player.ts +4 -0
  29. package/tests/fixtures/v4-game/src/modules/main/scene-map.ts +4 -0
  30. package/tests/fixtures/v4-game/src/modules/main/server.ts +4 -0
  31. package/tests/fixtures/v4-game/src/modules/main/sounds/theme.ogg +2 -0
  32. package/tests/fixtures/v4-game/src/modules/main/sounds/theme.ts +5 -0
  33. package/tests/fixtures/v4-game/src/modules/main/sprite.ts +4 -0
  34. package/tests/fixtures/v4-game/src/modules/main/worlds/maps/world-map.tmx +6 -0
  35. package/tests/fixtures/v4-game/src/modules/main/worlds/world.world +13 -0
  36. package/vite.config.ts +7 -1
package/dist/index.js CHANGED
@@ -1,10 +1,18 @@
1
- import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
2
- import { dirname, extname, join, relative } from "path";
3
- import { build, defineConfig } from "vite";
1
+ import fs, { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
2
+ import path, { dirname, extname, join, relative } from "path";
3
+ import { build, defineConfig, loadEnv } from "vite";
4
4
  import canvasengine from "@canvasengine/compiler";
5
5
  import dts from "vite-plugin-dts";
6
6
  import { existsSync as existsSync$1, rmSync } from "node:fs";
7
7
  import { resolve } from "node:path";
8
+ import { EventEmitter } from "node:events";
9
+ import "node:module";
10
+ import sizeOf from "image-size";
11
+ import * as parser from "@babel/parser";
12
+ import _traverse from "@babel/traverse";
13
+ import _generate from "@babel/generator";
14
+ import { identifier, importDeclaration, importDefaultSpecifier, stringLiteral } from "@babel/types";
15
+ import toml from "@iarna/toml";
8
16
  //#region src/tiled-map-folder-plugin.ts
9
17
  /**
10
18
  * Vite plugin that serves a data folder in development mode and copies it to assets during build
@@ -6801,7 +6809,7 @@ function replaceConfigImport() {
6801
6809
  };
6802
6810
  }
6803
6811
  //#endregion
6804
- //#region ../server/dist/module-BrK6VWLE.js
6812
+ //#region ../server/dist/module-CW79w6Lk.js
6805
6813
  /******************************************************************************
6806
6814
  Copyright (c) Microsoft Corporation.
6807
6815
 
@@ -8369,6 +8377,7 @@ var createSyncClass = (currentClass, parentKey = null, parentClass = null, path
8369
8377
  currentClass.$path = path;
8370
8378
  if (parentClass) currentClass.$valuesChanges = parentClass.$valuesChanges;
8371
8379
  if (parentKey) setMetadata(currentClass, "id", parentKey);
8380
+ applyDecoratedSignalMetadata(currentClass);
8372
8381
  if (currentClass.$snapshot) for (const key of currentClass.$snapshot.keys()) {
8373
8382
  const signal = currentClass.$snapshot.get(key);
8374
8383
  const syncToClient = signal.options?.syncToClient ?? true;
@@ -8391,8 +8400,20 @@ var createSyncClass = (currentClass, parentKey = null, parentClass = null, path
8391
8400
  });
8392
8401
  }
8393
8402
  };
8403
+ function applyDecoratedSignalMetadata(currentClass) {
8404
+ const syncMetadata = currentClass.constructor?._syncMetadata;
8405
+ if (!syncMetadata) return;
8406
+ for (const [propertyKey, options] of syncMetadata) {
8407
+ const signal = currentClass[propertyKey];
8408
+ if (!isSignal(signal) || signal.options) continue;
8409
+ currentClass[propertyKey] = type(signal, propertyKey, {
8410
+ ...options,
8411
+ skipInitialSync: true
8412
+ }, currentClass);
8413
+ }
8414
+ }
8394
8415
  var type = (_signal, path, options = {}, currentInstance) => {
8395
- const { syncToClient = true, persist: persist2 = true, transform } = options;
8416
+ const { syncToClient = true, persist: persist2 = true, transform, skipInitialSync = false } = options;
8396
8417
  let init = true;
8397
8418
  const handleObjectSubject = (value, propPath) => {
8398
8419
  const newPath = `${propPath}${value.key ? `.${value.key}` : ""}`;
@@ -8424,14 +8445,15 @@ var type = (_signal, path, options = {}, currentInstance) => {
8424
8445
  const savePath = (propPath, value) => {
8425
8446
  const transformedValue = transform && value !== "$delete" ? transform(value) : value;
8426
8447
  if (syncToClient) currentInstance.$valuesChanges.set(propPath, transformedValue);
8427
- if (persist2 && currentInstance.$path !== void 0) currentInstance.$valuesChanges.setPersist(transformedValue == "$delete" ? propPath : currentInstance.$path, transformedValue);
8448
+ if (persist2 && currentInstance.$path !== void 0) currentInstance.$valuesChanges.setPersist(propPath, transformedValue);
8428
8449
  };
8429
8450
  const setupSubscription = (signal, signalPath) => {
8430
8451
  if (!isSignal(signal)) return;
8431
- if (syncToClient && currentInstance.$valuesChanges) {
8452
+ if (syncToClient && !skipInitialSync && currentInstance.$valuesChanges) {
8432
8453
  const initialValue = signal();
8433
8454
  const transformedInitialValue = transform ? transform(initialValue) : initialValue;
8434
- currentInstance.$valuesChanges.set(signalPath, transformedInitialValue);
8455
+ const initialPath = currentInstance.$path !== void 0 ? `${currentInstance.$path ? `${currentInstance.$path}.` : ""}${signalPath}` : signalPath;
8456
+ currentInstance.$valuesChanges.set(initialPath, transformedInitialValue);
8435
8457
  }
8436
8458
  signal.options = options;
8437
8459
  signal.observable.subscribe((value) => {
@@ -8463,7 +8485,7 @@ var type = (_signal, path, options = {}, currentInstance) => {
8463
8485
  init = false;
8464
8486
  return _signal;
8465
8487
  };
8466
- function sync(options) {
8488
+ function normalizeSyncOptions(options) {
8467
8489
  let classType;
8468
8490
  let persist2 = true;
8469
8491
  let syncToClient = true;
@@ -8475,18 +8497,27 @@ function sync(options) {
8475
8497
  if (options.hasOwnProperty("syncToClient")) syncToClient = options.syncToClient;
8476
8498
  if (options.hasOwnProperty("transform")) transform = options.transform;
8477
8499
  }
8500
+ return {
8501
+ classType,
8502
+ persist: persist2,
8503
+ syncToClient,
8504
+ transform
8505
+ };
8506
+ }
8507
+ function setSyncMetadata(target, propertyKey, options) {
8508
+ if (!target.constructor._syncMetadata) target.constructor._syncMetadata = /* @__PURE__ */ new Map();
8509
+ target.constructor._syncMetadata.set(propertyKey, options);
8510
+ }
8511
+ function sync(options) {
8512
+ const normalizedOptions = normalizeSyncOptions(options);
8478
8513
  return function(target, propertyKey) {
8514
+ setSyncMetadata(target, propertyKey, normalizedOptions);
8479
8515
  const privatePropertyKey = `__${propertyKey}`;
8480
8516
  const getter = function() {
8481
8517
  return this[privatePropertyKey];
8482
8518
  };
8483
8519
  const setter = function(newVal) {
8484
- this[privatePropertyKey] = type(newVal, propertyKey, {
8485
- classType,
8486
- persist: persist2,
8487
- syncToClient,
8488
- transform
8489
- }, this);
8520
+ this[privatePropertyKey] = type(newVal, propertyKey, normalizedOptions, this);
8490
8521
  };
8491
8522
  Object.defineProperty(target, propertyKey, {
8492
8523
  get: getter,
@@ -8586,13 +8617,6 @@ var toCloneableSyncValue = (value, seen = /* @__PURE__ */ new WeakSet()) => {
8586
8617
  }
8587
8618
  return output;
8588
8619
  };
8589
- var Direction = /* @__PURE__ */ function(Direction) {
8590
- Direction["Up"] = "up";
8591
- Direction["Down"] = "down";
8592
- Direction["Left"] = "left";
8593
- Direction["Right"] = "right";
8594
- return Direction;
8595
- }({});
8596
8620
  var RpgCommonPlayer = class {
8597
8621
  constructor() {
8598
8622
  this.name = signal("");
@@ -8601,7 +8625,7 @@ var RpgCommonPlayer = class {
8601
8625
  this.y = signal(0);
8602
8626
  this.z = signal(0);
8603
8627
  this.tint = signal("white");
8604
- this.direction = signal(Direction.Down);
8628
+ this.direction = signal("down");
8605
8629
  this.speed = signal(4);
8606
8630
  this.graphics = signal([]);
8607
8631
  this.canMove = signal(true);
@@ -9345,7 +9369,6 @@ var Context = class {
9345
9369
  return this.values[key];
9346
9370
  }
9347
9371
  };
9348
- function setInject(_context) {}
9349
9372
  var context = new Context();
9350
9373
  context["side"] = "server";
9351
9374
  var MAP_UPDATE_TOKEN_HEADER = "x-rpgjs-map-update-token";
@@ -9853,50 +9876,457 @@ function logNetworkSimulationStatus() {
9853
9876
  console.log(`\x1b[36m[NETWORK SIMULATION]\x1b[0m Latency simulation: ${latencyStatus.ms}ms ping${filterInfo}`);
9854
9877
  } else console.log("\x1B[36m[NETWORK SIMULATION]\x1B[0m Latency simulation: disabled");
9855
9878
  }
9856
- var PartyRoom = class {
9857
- constructor(id) {
9858
- this.env = {};
9859
- this.context = {};
9879
+ var DEFAULT_PARTIES_PATH = "/parties/main";
9880
+ var WEBSOCKET_OPEN = 1;
9881
+ function createMemoryNodeRoomStorage(options = {}) {
9882
+ return new MemoryNodeRoomStorage(options);
9883
+ }
9884
+ function createNodeRoomTransport(ServerClass, options = {}) {
9885
+ return new NodeRoomTransport(ServerClass, options);
9886
+ }
9887
+ var MemoryNodeRoomStorage = class {
9888
+ constructor(options = {}) {
9889
+ this.rooms = /* @__PURE__ */ new Map();
9890
+ if (options.snapshot) this.restore(options.snapshot);
9891
+ }
9892
+ getStorage(namespace, roomId) {
9893
+ const key = getStorageKey(namespace, roomId);
9894
+ let memory = this.rooms.get(key);
9895
+ if (!memory) {
9896
+ memory = /* @__PURE__ */ new Map();
9897
+ this.rooms.set(key, memory);
9898
+ }
9899
+ return new MemoryNodeRoomStorageInstance(memory);
9900
+ }
9901
+ snapshot() {
9902
+ const snapshot = {};
9903
+ for (const [roomKey, memory] of this.rooms) {
9904
+ if (memory.size === 0) continue;
9905
+ snapshot[roomKey] = Array.from(memory.entries()).map(([key, value]) => [key, cloneStorageValue(value)]);
9906
+ }
9907
+ return snapshot;
9908
+ }
9909
+ restore(snapshot) {
9910
+ this.clear();
9911
+ for (const [roomKey, entries] of Object.entries(snapshot)) this.rooms.set(roomKey, new Map(entries.map(([key, value]) => [key, cloneStorageValue(value)])));
9912
+ }
9913
+ clear() {
9914
+ this.rooms.clear();
9915
+ }
9916
+ };
9917
+ var MemoryNodeRoomStorageInstance = class {
9918
+ constructor(memory) {
9919
+ this.memory = memory;
9920
+ }
9921
+ async put(keyOrEntries, value) {
9922
+ if (typeof keyOrEntries === "string") {
9923
+ this.memory.set(keyOrEntries, value);
9924
+ return;
9925
+ }
9926
+ for (const [key, entryValue] of Object.entries(keyOrEntries)) this.memory.set(key, entryValue);
9927
+ }
9928
+ async get(key) {
9929
+ return this.memory.get(key);
9930
+ }
9931
+ async delete(keyOrKeys) {
9932
+ if (Array.isArray(keyOrKeys)) {
9933
+ let deleted = 0;
9934
+ for (const key of keyOrKeys) if (this.memory.delete(key)) deleted += 1;
9935
+ return deleted;
9936
+ }
9937
+ return this.memory.delete(keyOrKeys);
9938
+ }
9939
+ async list(options = {}) {
9940
+ let entries = Array.from(this.memory.entries());
9941
+ if (options.prefix !== void 0) entries = entries.filter(([key]) => key.startsWith(options.prefix));
9942
+ if (options.start !== void 0) entries = entries.filter(([key]) => key >= options.start);
9943
+ if (options.startAfter !== void 0) entries = entries.filter(([key]) => key > options.startAfter);
9944
+ if (options.end !== void 0) entries = entries.filter(([key]) => key < options.end);
9945
+ entries.sort(([a], [b]) => a.localeCompare(b));
9946
+ if (options.reverse) entries.reverse();
9947
+ if (options.limit !== void 0) entries = entries.slice(0, options.limit);
9948
+ return new Map(entries);
9949
+ }
9950
+ };
9951
+ var NodeRoomTransport = class {
9952
+ constructor(ServerClass, options = {}) {
9953
+ this.ServerClass = ServerClass;
9954
+ this.records = /* @__PURE__ */ new Map();
9955
+ this.partiesPath = normalizePath(options.partiesPath ?? DEFAULT_PARTIES_PATH);
9956
+ this.env = options.env ?? {};
9957
+ this.rooms = {
9958
+ main: ServerClass,
9959
+ ...options.rooms ?? {}
9960
+ };
9961
+ this.storage = options.storage ?? createMemoryNodeRoomStorage();
9962
+ this.externalParties = options.externalParties ?? {};
9963
+ }
9964
+ async fetch(pathOrRequest, init) {
9965
+ const request = typeof pathOrRequest === "string" ? new Request(toLocalUrl(pathOrRequest), init) : pathOrRequest;
9966
+ const parsed = this.parsePartyRequest(request.url);
9967
+ if (!parsed) return new Response("Not Found", { status: 404 });
9968
+ return (await this.getRecord(parsed.namespace, parsed.roomId)).server.onRequest?.(request) ?? new Response("Not Found", { status: 404 });
9969
+ }
9970
+ async handleNodeRequest(req, res, next) {
9971
+ const url = getRequestUrl(req);
9972
+ if (!this.parsePartyRequest(url)) {
9973
+ if (next) {
9974
+ next();
9975
+ return;
9976
+ }
9977
+ await writeNodeResponse(res, new Response("Not Found", { status: 404 }));
9978
+ return;
9979
+ }
9980
+ try {
9981
+ const request = await createWebRequest(req, url);
9982
+ await writeNodeResponse(res, await this.fetch(request));
9983
+ } catch (error) {
9984
+ if (next) {
9985
+ next(error);
9986
+ return;
9987
+ }
9988
+ await writeNodeResponse(res, new Response("Internal Server Error", { status: 500 }));
9989
+ }
9990
+ }
9991
+ handleUpgrade(wsServer, request, socket, head) {
9992
+ const parsed = this.parsePartyRequest(getRequestUrl(request));
9993
+ if (!parsed) {
9994
+ socket.destroy();
9995
+ return;
9996
+ }
9997
+ wsServer.handleUpgrade(request, socket, head, (webSocket) => {
9998
+ this.acceptWebSocket(webSocket, request, parsed).catch(() => {
9999
+ webSocket.close(1011, "Unable to start room connection");
10000
+ });
10001
+ wsServer.emit?.("connection", webSocket, request);
10002
+ });
10003
+ }
10004
+ async acceptWebSocket(webSocket, request, parsedPath) {
10005
+ const url = request instanceof Request ? request.url : getRequestUrl(request);
10006
+ const parsed = parsedPath ?? this.parsePartyRequest(url);
10007
+ if (!parsed) {
10008
+ webSocket.close(1008, "Invalid room path");
10009
+ throw new Error(`Unable to route WebSocket URL: ${url}`);
10010
+ }
10011
+ const record = await this.getRecord(parsed.namespace, parsed.roomId);
10012
+ const connection = new NodeConnection(webSocket, url, getConnectionIdFromUrl(url));
10013
+ const connectRequest = request instanceof Request ? request : await createWebRequest(request, url, false);
10014
+ await record.server.onConnect?.(connection, { request: connectRequest });
10015
+ const onMessage = (data) => {
10016
+ record.server.onMessage?.(normalizeWebSocketMessage(data), connection);
10017
+ };
10018
+ const onClose = () => {
10019
+ record.room.deleteConnection(connection.id, connection);
10020
+ record.server.onClose?.(connection);
10021
+ };
10022
+ const onError = (error) => {
10023
+ record.server.onError?.(connection, error);
10024
+ };
10025
+ webSocket.on("message", onMessage);
10026
+ webSocket.on("close", onClose);
10027
+ webSocket.on("error", onError);
10028
+ record.room.addConnection(connection);
10029
+ return connection;
10030
+ }
10031
+ getRoom(namespace, roomId) {
10032
+ return this.getRecord(namespace, roomId).then((record) => record.room);
10033
+ }
10034
+ getNamespacePath(namespace, roomId) {
10035
+ return `/${[
10036
+ ...trimSlashes(this.getPartiesBase()).split("/").slice(0, -1),
10037
+ namespace,
10038
+ encodeURIComponent(roomId)
10039
+ ].join("/")}`;
10040
+ }
10041
+ async getRecord(namespace, roomId) {
10042
+ const key = `${namespace}:${roomId}`;
10043
+ const existing = this.records.get(key);
10044
+ if (existing) return existing;
10045
+ const recordPromise = this.createRecord(namespace, roomId);
10046
+ this.records.set(key, recordPromise);
10047
+ try {
10048
+ return await recordPromise;
10049
+ } catch (error) {
10050
+ this.records.delete(key);
10051
+ throw error;
10052
+ }
10053
+ }
10054
+ async createRecord(namespace, roomId) {
10055
+ const ServerClass = this.rooms[namespace] ?? this.ServerClass;
10056
+ const room = new NodeRoom({
10057
+ id: roomId,
10058
+ name: namespace,
10059
+ env: this.env,
10060
+ storage: await this.resolveStorage(namespace, roomId),
10061
+ transport: this
10062
+ });
10063
+ const server = new ServerClass(room);
10064
+ const record = {
10065
+ room,
10066
+ server,
10067
+ started: Promise.resolve(server.onStart?.()).then(() => void 0)
10068
+ };
10069
+ await record.started;
10070
+ return record;
10071
+ }
10072
+ async resolveStorage(namespace, roomId) {
10073
+ if (typeof this.storage === "function") return this.storage(namespace, roomId);
10074
+ return this.storage.getStorage(namespace, roomId);
10075
+ }
10076
+ parsePartyRequest(url) {
10077
+ const requestUrl = new URL(url, "http://localhost");
10078
+ const partiesBase = this.getPartiesBase();
10079
+ const segments = trimSlashes(requestUrl.pathname).split("/");
10080
+ const baseSegments = trimSlashes(partiesBase).split("/").slice(0, -1);
10081
+ if (segments.length < baseSegments.length + 2) return null;
10082
+ for (let index = 0; index < baseSegments.length; index++) if (segments[index] !== baseSegments[index]) return null;
10083
+ const namespace = decodeURIComponent(segments[baseSegments.length]);
10084
+ const roomId = decodeURIComponent(segments[baseSegments.length + 1]);
10085
+ const rest = segments.slice(baseSegments.length + 2).join("/");
10086
+ return {
10087
+ namespace,
10088
+ roomId,
10089
+ restPath: rest ? `/${rest}` : "/"
10090
+ };
10091
+ }
10092
+ getPartiesBase() {
10093
+ return this.partiesPath;
10094
+ }
10095
+ };
10096
+ var NodeRoom = class {
10097
+ constructor(options) {
9860
10098
  this.connections = /* @__PURE__ */ new Map();
9861
- this.storageData = /* @__PURE__ */ new Map();
9862
- this.id = id;
9863
- this.internalID = `internal_${id}_${Date.now()}`;
10099
+ this.analytics = {};
10100
+ this.id = options.id;
10101
+ this.internalID = `${options.name}:${options.id}`;
10102
+ this.name = options.name;
10103
+ this.env = options.env;
10104
+ this.storage = options.storage;
10105
+ this.parties = createPartiesContext(options.transport);
10106
+ this.context = {
10107
+ parties: this.parties,
10108
+ ai: {},
10109
+ vectorize: {},
10110
+ analytics: this.analytics,
10111
+ assets: { fetch: async () => null },
10112
+ bindings: {
10113
+ r2: {},
10114
+ kv: {}
10115
+ }
10116
+ };
10117
+ }
10118
+ blockConcurrencyWhile(callback) {
10119
+ return callback();
9864
10120
  }
9865
- async broadcast(message, except = []) {
9866
- const data = typeof message === "string" ? message : JSON.stringify(message);
9867
- const sendPromises = [];
9868
- for (const [connectionId, connection] of this.connections) if (!except.includes(connectionId)) sendPromises.push(connection.send(data));
9869
- await Promise.all(sendPromises);
10121
+ broadcast(msg, without = []) {
10122
+ for (const connection of this.connections.values()) if (!without.includes(connection.id)) connection.send(msg);
9870
10123
  }
9871
10124
  getConnection(id) {
9872
- return this.connections.get(id);
10125
+ let connection;
10126
+ for (const current of this.connections.values()) if (current.id === id || current.sessionId === id) connection = current;
10127
+ return connection;
9873
10128
  }
9874
- getConnections(tag) {
9875
- return this.connections.values();
10129
+ getConnections() {
10130
+ return Array.from(this.connections.values());
9876
10131
  }
9877
10132
  addConnection(connection) {
9878
10133
  this.connections.set(connection.id, connection);
9879
10134
  }
9880
- removeConnection(connectionId) {
9881
- this.connections.delete(connectionId);
10135
+ deleteConnection(id, connection) {
10136
+ if (connection) {
10137
+ this.connections.delete(connection.id);
10138
+ return;
10139
+ }
10140
+ for (const [connectionKey, current] of this.connections) if (current.id === id || current.sessionId === id) this.connections.delete(connectionKey);
9882
10141
  }
9883
- get storage() {
9884
- return {
9885
- put: async (key, value) => {
9886
- this.storageData.set(key, value);
9887
- },
9888
- get: async (key) => {
9889
- return this.storageData.get(key);
9890
- },
9891
- delete: async (key) => {
9892
- this.storageData.delete(key);
9893
- },
9894
- list: async () => {
9895
- return Array.from(this.storageData.entries());
9896
- }
9897
- };
10142
+ };
10143
+ var NodeConnection = class {
10144
+ constructor(webSocket, uri, sessionId) {
10145
+ this.webSocket = webSocket;
10146
+ this.id = createConnectionId();
10147
+ this.socket = this;
10148
+ this.state = null;
10149
+ this.attachment = null;
10150
+ this.sessionId = sessionId || this.id;
10151
+ this.uri = uri;
10152
+ }
10153
+ send(data) {
10154
+ if (this.webSocket.readyState === void 0 || this.webSocket.readyState === WEBSOCKET_OPEN) this.webSocket.send(data);
10155
+ }
10156
+ close(code, reason) {
10157
+ this.webSocket.close(code, reason);
10158
+ }
10159
+ setState(state) {
10160
+ this.state = typeof state === "function" ? state(this.state) : state;
10161
+ return this.state;
10162
+ }
10163
+ serializeAttachment(attachment) {
10164
+ this.attachment = attachment;
10165
+ }
10166
+ deserializeAttachment() {
10167
+ return this.attachment;
9898
10168
  }
9899
10169
  };
10170
+ function createPartiesContext(transport) {
10171
+ return new Proxy({}, { get(_target, namespace) {
10172
+ const externalNamespace = transport.externalParties[namespace];
10173
+ if (externalNamespace) return externalNamespace;
10174
+ return { get(roomId) {
10175
+ return {
10176
+ connect: () => {
10177
+ throw new Error("Party stub connect() is not implemented by @signe/room/node");
10178
+ },
10179
+ async socket(pathOrInit, init) {
10180
+ const path = typeof pathOrInit === "string" ? pathOrInit : "/";
10181
+ const requestInit = typeof pathOrInit === "string" ? init : pathOrInit;
10182
+ const request = new Request(toLocalUrl(`${transport.getNamespacePath(namespace, roomId)}${normalizeStubPath(path)}`), requestInit);
10183
+ const pair = createInMemoryWebSocketPair();
10184
+ transport.acceptWebSocket(pair.server, request).catch(() => {
10185
+ pair.client.close(1011, "Unable to start room connection");
10186
+ });
10187
+ return pair.client;
10188
+ },
10189
+ fetch(pathOrInit, init) {
10190
+ const path = typeof pathOrInit === "string" ? pathOrInit : "/";
10191
+ const requestInit = typeof pathOrInit === "string" ? init : pathOrInit;
10192
+ return transport.fetch(`${transport.getNamespacePath(namespace, roomId)}${normalizeStubPath(path)}`, requestInit);
10193
+ }
10194
+ };
10195
+ } };
10196
+ } });
10197
+ }
10198
+ function createInMemoryWebSocketPair() {
10199
+ const client = new InMemoryWebSocket();
10200
+ const server = new InMemoryWebSocket();
10201
+ client.setPeer(server);
10202
+ server.setPeer(client);
10203
+ return {
10204
+ client,
10205
+ server
10206
+ };
10207
+ }
10208
+ var InMemoryWebSocket = class {
10209
+ constructor() {
10210
+ this.readyState = WEBSOCKET_OPEN;
10211
+ this.emitter = new EventEmitter();
10212
+ }
10213
+ setPeer(peer) {
10214
+ this.peer = peer;
10215
+ }
10216
+ send(data, cb) {
10217
+ if (this.readyState !== WEBSOCKET_OPEN || this.peer?.readyState !== WEBSOCKET_OPEN) {
10218
+ cb?.(/* @__PURE__ */ new Error("WebSocket is not open"));
10219
+ return;
10220
+ }
10221
+ queueMicrotask(() => {
10222
+ this.peer?.emitMessage(data);
10223
+ cb?.();
10224
+ });
10225
+ }
10226
+ close(code, reason) {
10227
+ if (this.readyState !== WEBSOCKET_OPEN) return;
10228
+ this.readyState = 3;
10229
+ this.emitter.emit("close", code, reason);
10230
+ if (this.peer?.readyState === WEBSOCKET_OPEN) {
10231
+ this.peer.readyState = 3;
10232
+ this.peer.emitter.emit("close", code, reason);
10233
+ }
10234
+ }
10235
+ on(event, listener) {
10236
+ this.emitter.on(event, listener);
10237
+ return this;
10238
+ }
10239
+ off(event, listener) {
10240
+ this.emitter.off(event, listener);
10241
+ return this;
10242
+ }
10243
+ removeListener(event, listener) {
10244
+ this.emitter.removeListener(event, listener);
10245
+ return this;
10246
+ }
10247
+ addEventListener(type, listener) {
10248
+ if (type === "message") {
10249
+ this.on("message", (data) => listener({ data }));
10250
+ return;
10251
+ }
10252
+ this.on(type, (event) => listener(event));
10253
+ }
10254
+ emitMessage(data) {
10255
+ if (this.readyState === WEBSOCKET_OPEN) this.emitter.emit("message", data);
10256
+ }
10257
+ };
10258
+ async function createWebRequest(req, url, includeBody = true) {
10259
+ const headers = new Headers();
10260
+ for (const [key, value] of Object.entries(req.headers)) if (Array.isArray(value)) for (const item of value) headers.append(key, item);
10261
+ else if (value !== void 0) headers.set(key, String(value));
10262
+ const method = req.method ?? "GET";
10263
+ const body = includeBody && !["GET", "HEAD"].includes(method) ? await readIncomingBody(req) : void 0;
10264
+ return new Request(url, {
10265
+ method,
10266
+ headers,
10267
+ body
10268
+ });
10269
+ }
10270
+ async function readIncomingBody(req) {
10271
+ const chunks = [];
10272
+ for await (const chunk of req) if (typeof chunk === "string") chunks.push(new TextEncoder().encode(chunk));
10273
+ else chunks.push(chunk);
10274
+ const size = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
10275
+ const body = new Uint8Array(size);
10276
+ let offset = 0;
10277
+ for (const chunk of chunks) {
10278
+ body.set(chunk, offset);
10279
+ offset += chunk.byteLength;
10280
+ }
10281
+ return body;
10282
+ }
10283
+ async function writeNodeResponse(res, response) {
10284
+ res.statusCode = response.status;
10285
+ response.headers.forEach((value, key) => {
10286
+ res.setHeader(key, value);
10287
+ });
10288
+ const body = new Uint8Array(await response.arrayBuffer());
10289
+ res.end(body);
10290
+ }
10291
+ function getRequestUrl(req) {
10292
+ return `${req.headers["x-forwarded-proto"] ?? "http"}://${req.headers.host ?? "localhost"}${req.url ?? "/"}`;
10293
+ }
10294
+ function normalizeWebSocketMessage(data) {
10295
+ if (typeof data === "string") return data;
10296
+ if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
10297
+ if (ArrayBuffer.isView(data)) return new TextDecoder().decode(data);
10298
+ return String(data);
10299
+ }
10300
+ function normalizePath(path) {
10301
+ return `/${trimSlashes(path)}`;
10302
+ }
10303
+ function normalizeStubPath(path) {
10304
+ if (!path || path === "/") return "";
10305
+ return path.startsWith("/") ? path : `/${path}`;
10306
+ }
10307
+ function toLocalUrl(path) {
10308
+ return path.startsWith("http://") || path.startsWith("https://") ? path : `http://localhost${path.startsWith("/") ? path : `/${path}`}`;
10309
+ }
10310
+ function getConnectionIdFromUrl(url) {
10311
+ return new URL(url).searchParams.get("id")?.trim() || void 0;
10312
+ }
10313
+ function trimSlashes(value) {
10314
+ return value.replace(/^\/+|\/+$/g, "");
10315
+ }
10316
+ function getStorageKey(namespace, roomId) {
10317
+ return `${encodeURIComponent(namespace)}:${encodeURIComponent(roomId)}`;
10318
+ }
10319
+ function cloneStorageValue(value) {
10320
+ if (typeof structuredClone === "function") try {
10321
+ return structuredClone(value);
10322
+ } catch {
10323
+ return value;
10324
+ }
10325
+ return value;
10326
+ }
10327
+ function createConnectionId() {
10328
+ return Math.random().toString(36).slice(2, 12);
10329
+ }
9900
10330
  function normalizePathPrefix(path, fallback) {
9901
10331
  const trimmed = (path || fallback).trim();
9902
10332
  if (!trimmed) return fallback;
@@ -10001,25 +10431,12 @@ function resolveUrlFromSocketRequest(request) {
10001
10431
  url
10002
10432
  };
10003
10433
  }
10004
- function createConnectionContext(url, headers, method) {
10005
- const normalizedHeaders = /* @__PURE__ */ new Map();
10006
- headers.forEach((value, key) => {
10007
- normalizedHeaders.set(key.toLowerCase(), value);
10008
- });
10009
- return {
10010
- request: {
10011
- headers: {
10012
- has: (name) => normalizedHeaders.has(name.toLowerCase()),
10013
- get: (name) => normalizedHeaders.get(name.toLowerCase()),
10014
- entries: () => normalizedHeaders.entries(),
10015
- keys: () => normalizedHeaders.keys(),
10016
- values: () => normalizedHeaders.values()
10017
- },
10018
- method,
10019
- url: url.toString()
10020
- },
10021
- url
10022
- };
10434
+ function ensureNodeSessionIdQuery(url) {
10435
+ if (!url.searchParams.has("id")) {
10436
+ const partySocketId = url.searchParams.get("_pk");
10437
+ if (partySocketId) url.searchParams.set("id", partySocketId);
10438
+ }
10439
+ return url;
10023
10440
  }
10024
10441
  var RpgServerTransport = class {
10025
10442
  constructor(serverModule, options = {}) {
@@ -10032,10 +10449,33 @@ var RpgServerTransport = class {
10032
10449
  this.mapUpdateToken = resolveMapUpdateToken(options.mapUpdateToken);
10033
10450
  this.partiesPath = normalizePathPrefix(options.partiesPath || "/parties/main", "/parties/main");
10034
10451
  this.tiledBasePaths = options.tiledBasePaths;
10452
+ const owner = this;
10453
+ class RpgNodeServer extends serverModule {
10454
+ constructor(room) {
10455
+ super(room);
10456
+ owner.rooms.set(room.id, room);
10457
+ owner.servers.set(room.id, this);
10458
+ console.log(`Created new server instance for room: ${room.id}`);
10459
+ }
10460
+ async onStart() {
10461
+ await owner.ensureServerContext();
10462
+ await super.onStart?.();
10463
+ const roomId = this.room?.id;
10464
+ console.log(`Server started for room: ${roomId}`);
10465
+ if (owner.initializeMaps && roomId) await updateMap(roomId, this, {
10466
+ host: owner.lastKnownHost,
10467
+ mapUpdateToken: owner.mapUpdateToken,
10468
+ tiledBasePaths: owner.tiledBasePaths
10469
+ });
10470
+ }
10471
+ }
10472
+ this.transport = createNodeRoomTransport(RpgNodeServer, {
10473
+ partiesPath: this.partiesPath,
10474
+ storage: createMemoryNodeRoomStorage()
10475
+ });
10035
10476
  }
10036
10477
  async ensureServerContext() {
10037
10478
  if (this.serverContextInitialized) return;
10038
- setInject(context);
10039
10479
  await injector(context, [provideServerModules([])]);
10040
10480
  this.serverContextInitialized = true;
10041
10481
  }
@@ -10047,52 +10487,17 @@ var RpgServerTransport = class {
10047
10487
  }
10048
10488
  async ensureRoomAndServer(roomId, host) {
10049
10489
  if (host) this.lastKnownHost = host;
10050
- let room = this.rooms.get(roomId);
10051
- if (!room) {
10052
- room = new PartyRoom(roomId);
10053
- this.rooms.set(roomId, room);
10054
- console.log(`Created new room: ${roomId}`);
10055
- }
10056
- let rpgServer = this.servers.get(roomId);
10057
- if (!rpgServer) {
10058
- await this.ensureServerContext();
10059
- rpgServer = new this.serverModule(room);
10060
- this.servers.set(roomId, rpgServer);
10061
- console.log(`Created new server instance for room: ${roomId}`);
10062
- if (typeof rpgServer.onStart === "function") try {
10063
- await rpgServer.onStart();
10064
- console.log(`Server started for room: ${roomId}`);
10065
- } catch (error) {
10066
- console.error(`Error starting server for room ${roomId}:`, error);
10067
- }
10068
- if (this.initializeMaps) await updateMap(roomId, rpgServer, {
10069
- host: host || this.lastKnownHost,
10070
- mapUpdateToken: this.mapUpdateToken,
10071
- tiledBasePaths: this.tiledBasePaths
10072
- });
10073
- }
10074
- room.context.parties = this.buildPartiesContext();
10490
+ await this.transport.getRoom("main", roomId);
10491
+ const room = this.rooms.get(roomId);
10492
+ const rpgServer = this.servers.get(roomId);
10493
+ if (!room || !rpgServer) throw new Error(`Unable to initialize room: ${roomId}`);
10075
10494
  return {
10076
10495
  room,
10077
10496
  rpgServer
10078
10497
  };
10079
10498
  }
10080
- buildPartiesContext() {
10081
- return { main: { get: async (targetRoomId) => {
10082
- return { fetch: async (path, init) => {
10083
- const method = (init?.method || "GET").toUpperCase();
10084
- const headers = toHeaders(init?.headers);
10085
- const requestPath = path.startsWith("/") ? path : `/${path}`;
10086
- let bodyText = "";
10087
- if (typeof init?.body === "string") bodyText = init.body;
10088
- else if (typeof init?.body !== "undefined") bodyText = JSON.stringify(init.body);
10089
- return this.dispatchRoomRequest(targetRoomId, createRequestLike(`http://localhost${this.partiesPath}/${targetRoomId}${requestPath}`, method, headers, bodyText), this.lastKnownHost);
10090
- } };
10091
- } } };
10092
- }
10093
10499
  async dispatchRoomRequest(roomId, requestLike, host) {
10094
- const { room, rpgServer } = await this.ensureRoomAndServer(roomId, host);
10095
- room.context.parties = this.buildPartiesContext();
10500
+ const { rpgServer } = await this.ensureRoomAndServer(roomId, host);
10096
10501
  return normalizeEngineResponse(await rpgServer.onRequest?.(requestLike));
10097
10502
  }
10098
10503
  async fetch(request, init) {
@@ -10125,8 +10530,7 @@ var RpgServerTransport = class {
10125
10530
  next?.();
10126
10531
  return false;
10127
10532
  }
10128
- const bodyText = await readNodeBody(req);
10129
- await sendNodeResponse(res, await this.dispatchRoomRequest(route.roomId, createRequestLike(normalizedUrl.toString(), (req.method || "GET").toUpperCase(), headers, bodyText), host));
10533
+ await sendNodeResponse(res, await this.dispatchRoomRequest(route.roomId, createRequestLike(normalizedUrl.toString(), (req.method || "GET").toUpperCase(), headers, await readNodeBody(req)), host));
10130
10534
  return true;
10131
10535
  } catch (error) {
10132
10536
  console.error("Error handling RPG-JS request:", error);
@@ -10144,51 +10548,17 @@ var RpgServerTransport = class {
10144
10548
  console.log(`WebSocket upgrade request: ${normalizedRequest.url.pathname}`);
10145
10549
  const queryParams = Object.fromEntries(normalizedRequest.url.searchParams.entries());
10146
10550
  console.log(`Room: ${route.roomId}, Query params:`, queryParams);
10147
- const { room, rpgServer } = await this.ensureRoomAndServer(route.roomId, normalizedRequest.url.host);
10148
- room.context.parties = this.buildPartiesContext();
10149
- const connection = new PartyConnection(ws, queryParams._pk, normalizedRequest.rawUrl);
10150
- room.addConnection(connection);
10151
- console.log(`WebSocket connection established: ${connection.id} in room: ${route.roomId}`);
10152
- let isClosed = false;
10153
- const cleanup = async (logMessage, error) => {
10154
- if (isClosed) return;
10155
- isClosed = true;
10156
- if (logMessage) console.log(logMessage);
10157
- if (error) console.error("WebSocket error:", error);
10158
- room.removeConnection(connection.id);
10159
- await rpgServer.onClose?.(connection);
10160
- };
10161
- ws.on("message", async (data) => {
10162
- try {
10163
- const rawMessage = typeof data === "string" ? data : data.toString();
10164
- if (PartyConnection.packetLossEnabled && PartyConnection.packetLossRate > 0) {
10165
- if (!PartyConnection.packetLossFilter || rawMessage.includes(PartyConnection.packetLossFilter)) {
10166
- if (Math.random() < PartyConnection.packetLossRate) {
10167
- console.log(`\x1b[31m[PACKET LOSS]\x1b[0m Connection ${connection.id}: Server dropped an incoming packet (${(PartyConnection.packetLossRate * 100).toFixed(1)}% loss rate)`);
10168
- console.log(`\x1b[33m[PACKET DATA]\x1b[0m ${rawMessage.slice(0, 100)}${rawMessage.length > 100 ? "..." : ""}`);
10169
- return;
10170
- }
10171
- }
10172
- }
10173
- connection.bufferIncoming(rawMessage, async (batch) => {
10174
- for (const message of batch) await rpgServer.onMessage?.(message, connection);
10175
- });
10176
- } catch (error) {
10177
- console.error("Error processing WebSocket message:", error);
10178
- }
10179
- });
10180
- ws.on("close", () => {
10181
- cleanup(`WebSocket connection closed: ${connection.id} from room: ${route.roomId}`);
10182
- });
10183
- ws.on("error", (error) => {
10184
- cleanup(void 0, error);
10185
- });
10186
- if (typeof rpgServer.onConnect === "function") await rpgServer.onConnect(connection, createConnectionContext(normalizedRequest.url, normalizedRequest.headers, normalizedRequest.method));
10187
- await connection.send({
10551
+ this.lastKnownHost = normalizedRequest.url.host;
10552
+ ensureNodeSessionIdQuery(normalizedRequest.url);
10553
+ const connection = await this.transport.acceptWebSocket(ws, new Request(normalizedRequest.url.toString(), {
10554
+ headers: normalizedRequest.headers,
10555
+ method: normalizedRequest.method || "GET"
10556
+ }));
10557
+ await connection.send(JSON.stringify({
10188
10558
  type: "connected",
10189
10559
  id: connection.id,
10190
10560
  message: "Connected to RPG-JS server"
10191
- });
10561
+ }));
10192
10562
  return true;
10193
10563
  } catch (error) {
10194
10564
  console.error("Error establishing WebSocket connection:", error);
@@ -10198,7 +10568,11 @@ var RpgServerTransport = class {
10198
10568
  }
10199
10569
  async handleUpgrade(wsServer, request, socket, head) {
10200
10570
  const host = toHeaders(request.headers).get("host") || "localhost";
10201
- if (!parseSocketRoute(new URL(request.url || "/", `http://${host}`).pathname, this.partiesPath)) return false;
10571
+ const url = new URL(request.url || "/", `http://${host}`);
10572
+ if (!parseSocketRoute(url.pathname, this.partiesPath)) return false;
10573
+ ensureNodeSessionIdQuery(url);
10574
+ request.url = `${url.pathname}${url.search}`;
10575
+ this.lastKnownHost = host;
10202
10576
  wsServer.handleUpgrade(request, socket, head, (ws) => {
10203
10577
  this.acceptWebSocket(ws, request);
10204
10578
  });
@@ -10210,7 +10584,7 @@ function createRpgServerTransport(serverModule, options) {
10210
10584
  }
10211
10585
  //#endregion
10212
10586
  //#region src/server-plugin.ts
10213
- async function importWebSocketServer() {
10587
+ async function importWebSocketServer$1() {
10214
10588
  if (typeof process === "undefined" || !process.versions?.node) {
10215
10589
  console.warn("Not in Node.js environment, WebSocket server not available");
10216
10590
  return null;
@@ -10231,7 +10605,7 @@ function serverPlugin(serverModule) {
10231
10605
  name: "server-plugin",
10232
10606
  async configureServer(server) {
10233
10607
  try {
10234
- const WebSocketServerClass = await importWebSocketServer();
10608
+ const WebSocketServerClass = await importWebSocketServer$1();
10235
10609
  if (WebSocketServerClass) {
10236
10610
  wsServer = new WebSocketServerClass({ noServer: true });
10237
10611
  console.log("WebSocket server initialized successfully");
@@ -10432,6 +10806,797 @@ function rpgjs({ server, entryPoints }) {
10432
10806
  ];
10433
10807
  }
10434
10808
  //#endregion
10435
- export { directivePlugin, entryPointPlugin, removeImportsPlugin, replaceConfigImport, rpgjs, rpgjsModuleViteConfig, serverPlugin, tiledMapFolderPlugin };
10809
+ //#region src/compatibility-v4/flag-transform.ts
10810
+ function flagTransform(options = {}) {
10811
+ function getImportSide(importer) {
10812
+ if (importer?.includes("?server") || importer?.includes("server-entry")) return "server";
10813
+ if (importer?.includes("?client") || importer?.includes("client-entry") || importer?.includes("standalone-entry")) return "client";
10814
+ return options.side ?? "client";
10815
+ }
10816
+ function hasFlag(id, flag) {
10817
+ return id.endsWith(`?${flag}`) || id.includes(`?${flag}&`);
10818
+ }
10819
+ function getSideFromId(id) {
10820
+ return id.match(/[?&]side=(client|server)/)?.[1] === "server" ? "server" : "client";
10821
+ }
10822
+ return {
10823
+ name: "rpgjs-v4-flag-transform",
10824
+ async resolveId(source, importer, resolveOptions) {
10825
+ for (const flag of [
10826
+ "client!",
10827
+ "server!",
10828
+ "rpg!",
10829
+ "mmorpg!",
10830
+ "production!",
10831
+ "development!"
10832
+ ]) {
10833
+ if (!source.startsWith(flag)) continue;
10834
+ const id = source.slice(flag.length);
10835
+ const resolution = await this.resolve(id, importer, {
10836
+ skipSelf: true,
10837
+ ...resolveOptions
10838
+ });
10839
+ if (!resolution) return null;
10840
+ const importSide = getImportSide(importer);
10841
+ return {
10842
+ ...resolution,
10843
+ id: `${resolution.id}?${flag.slice(0, -1)}&side=${importSide}`
10844
+ };
10845
+ }
10846
+ return null;
10847
+ },
10848
+ transform(code, id) {
10849
+ const { mode = "development", type = "mmorpg" } = options;
10850
+ const side = getSideFromId(id);
10851
+ if (mode === "test") return {
10852
+ code,
10853
+ map: null
10854
+ };
10855
+ if (hasFlag(id, side === "client" ? "server" : "client") && type !== "rpg") return {
10856
+ code: "export default null;",
10857
+ map: null
10858
+ };
10859
+ if (hasFlag(id, "production") && mode !== "production" || hasFlag(id, "development") && mode !== "development" || hasFlag(id, "rpg") && type !== "rpg" || hasFlag(id, "mmorpg") && type !== "mmorpg") return {
10860
+ code: "export default null;",
10861
+ map: null
10862
+ };
10863
+ return {
10864
+ code,
10865
+ map: null
10866
+ };
10867
+ }
10868
+ };
10869
+ }
10870
+ //#endregion
10871
+ //#region src/compatibility-v4/require-transform.ts
10872
+ var traverse = _traverse.default ?? _traverse;
10873
+ var generate = _generate.default ?? _generate;
10874
+ function readStaticRequireArg(arg, ast) {
10875
+ if (!arg) return "";
10876
+ if (arg.type === "StringLiteral") return arg.value;
10877
+ if (arg.type === "Identifier") {
10878
+ let value = "";
10879
+ traverse(ast, { VariableDeclarator(path) {
10880
+ if (path.node.id?.name === arg.name && path.node.init?.type === "StringLiteral") value = path.node.init.value;
10881
+ } });
10882
+ return value;
10883
+ }
10884
+ if (arg.type === "BinaryExpression" && arg.operator === "+") {
10885
+ const left = readStaticRequireArg(arg.left, ast);
10886
+ const right = readStaticRequireArg(arg.right, ast);
10887
+ return left && right ? left + right : "";
10888
+ }
10889
+ return "";
10890
+ }
10891
+ function vitePluginRequire() {
10892
+ return {
10893
+ name: "rpgjs-v4-require-transform",
10894
+ transform(code, id) {
10895
+ if (!/(.jsx?|.tsx?)(\?.*)?$/.test(id) || !/^(?!.*node_modules(?:\/|\\)(?!rpgjs-|@rpgjs)).*$/.test(id)) return {
10896
+ code,
10897
+ map: null
10898
+ };
10899
+ const ast = parser.parse(code, {
10900
+ sourceType: "module",
10901
+ plugins: [
10902
+ "typescript",
10903
+ "jsx",
10904
+ "decorators-legacy",
10905
+ "classProperties"
10906
+ ]
10907
+ });
10908
+ let changed = false;
10909
+ traverse(ast, { CallExpression(path) {
10910
+ if (!path.node.callee || path.node.callee.type !== "Identifier" || path.node.callee.name !== "require") return;
10911
+ const request = readStaticRequireArg(path.node.arguments[0], ast);
10912
+ if (!request) return;
10913
+ const variableName = `__rpgjs_v4_require_${path.scope.generateUidIdentifier("asset").name}`;
10914
+ ast.program.body.unshift(importDeclaration([importDefaultSpecifier(identifier(variableName))], stringLiteral(request)));
10915
+ path.replaceWith(identifier(variableName));
10916
+ changed = true;
10917
+ } });
10918
+ if (!changed) return {
10919
+ code,
10920
+ map: null
10921
+ };
10922
+ return {
10923
+ code: generate(ast, {}).code,
10924
+ map: null
10925
+ };
10926
+ }
10927
+ };
10928
+ }
10929
+ //#endregion
10930
+ //#region src/compatibility-v4/utils.ts
10931
+ function dedent(strings, ...values) {
10932
+ const fullString = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ""), "");
10933
+ const lines = fullString.split("\n");
10934
+ let minIndent = Infinity;
10935
+ for (const line of lines) {
10936
+ if (!line.trim()) continue;
10937
+ minIndent = Math.min(minIndent, line.match(/^\s*/)?.[0].length ?? 0);
10938
+ }
10939
+ if (minIndent === Infinity) return fullString.trim();
10940
+ return lines.map((line) => line.trim() ? line.slice(minIndent) : line).join("\n").trim();
10941
+ }
10942
+ function warn(message) {
10943
+ console.warn(`[RPG-JS v4 compatibility] ${message}`);
10944
+ }
10945
+ function toPosix(value) {
10946
+ return value.replace(/\\/g, "/");
10947
+ }
10948
+ function formatVariableName(value) {
10949
+ return value.replace(/\./g, "").replace(/[.@/\\ -]/g, "_").replace(/[^A-Za-z0-9_$]/g, "_");
10950
+ }
10951
+ function transformPathIfModule(moduleName) {
10952
+ if (moduleName.startsWith("@rpgjs") || moduleName.startsWith("rpgjs-")) return path.join("node_modules", moduleName);
10953
+ return moduleName;
10954
+ }
10955
+ function resolveModuleImport(moduleName) {
10956
+ return moduleName.replace(/^\.\//, "");
10957
+ }
10958
+ function getAllFiles(dirPath) {
10959
+ if (!fs.existsSync(dirPath)) return [];
10960
+ const files = [];
10961
+ const dirents = fs.readdirSync(dirPath, { withFileTypes: true });
10962
+ for (const dirent of dirents) {
10963
+ const fullPath = path.join(dirPath, dirent.name);
10964
+ if (dirent.isDirectory()) files.push(...getAllFiles(fullPath));
10965
+ else files.push(fullPath);
10966
+ }
10967
+ return files;
10968
+ }
10969
+ function importPathForFile(file, root) {
10970
+ const srcPath = path.join(root, "src");
10971
+ if (file.startsWith(srcPath + path.sep)) return `@/${toPosix(path.relative(srcPath, file))}`;
10972
+ return `./${toPosix(path.relative(root, file))}`;
10973
+ }
10974
+ function importString(modulePath, fileName, variableName = fileName, projectRoot = process.cwd()) {
10975
+ const file = path.resolve(projectRoot, transformPathIfModule(modulePath), `${fileName}.ts`);
10976
+ if (!fs.existsSync(file)) return "";
10977
+ return `import ${variableName} from '${importPathForFile(file, projectRoot)}'`;
10978
+ }
10979
+ function searchFolderAndTransformToImportString(folderPath, modulePath, extensionFilter, returnCb, options, projectRoot = process.cwd()) {
10980
+ const folder = path.resolve(projectRoot, transformPathIfModule(modulePath), folderPath);
10981
+ if (!fs.existsSync(folder)) return {
10982
+ variablesString: "",
10983
+ importString: "",
10984
+ folder: "",
10985
+ relativePath: ""
10986
+ };
10987
+ const extensions = Array.isArray(extensionFilter) ? extensionFilter : [extensionFilter];
10988
+ let importString = "";
10989
+ let relativePath = "";
10990
+ return {
10991
+ variablesString: getAllFiles(folder).filter((file) => extensions.some((ext) => file.endsWith(ext))).filter((file) => options?.customFilter ? options.customFilter(file) : true).map((file) => {
10992
+ const importPath = importPathForFile(file, projectRoot);
10993
+ const variableName = formatVariableName(importPath);
10994
+ relativePath = importPath;
10995
+ importString += `\nimport ${variableName} from '${importPath}'`;
10996
+ return returnCb ? returnCb(importPath, variableName, file) : variableName;
10997
+ }).join(","),
10998
+ importString,
10999
+ folder,
11000
+ relativePath
11001
+ };
11002
+ }
11003
+ function replaceEnvVars(obj, envs) {
11004
+ if (obj == null) return obj;
11005
+ if (typeof obj === "string" && obj.startsWith("$ENV:")) return envs[obj.slice(5)];
11006
+ if (Array.isArray(obj)) return obj.map((item) => replaceEnvVars(item, envs));
11007
+ if (typeof obj === "object") return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, replaceEnvVars(value, envs)]));
11008
+ return obj;
11009
+ }
11010
+ //#endregion
11011
+ //#region src/compatibility-v4/load-config-file.ts
11012
+ function loadConfigFileSync(mode = "development", root = process.cwd()) {
11013
+ const tomlFile = path.resolve(root, "rpg.toml");
11014
+ const jsonFile = path.resolve(root, "rpg.json");
11015
+ let config = {};
11016
+ if (fs.existsSync(tomlFile)) config = toml.parse(fs.readFileSync(tomlFile, "utf8"));
11017
+ else if (fs.existsSync(jsonFile)) config = JSON.parse(fs.readFileSync(jsonFile, "utf8"));
11018
+ config = replaceEnvVars(config, loadEnv(mode, root, ""));
11019
+ config.autostart = config.autostart ?? true;
11020
+ config.modulesRoot = config.modulesRoot ?? "";
11021
+ config.compilerOptions ??= {};
11022
+ config.compilerOptions.build ??= {};
11023
+ config.compilerOptions.build.pwaEnabled ??= true;
11024
+ config.compilerOptions.build.outputDir ??= "dist";
11025
+ if (config.modules) config.modules = config.modules.map((module) => {
11026
+ if (module.startsWith(".")) return "./" + path.join(config.modulesRoot, module);
11027
+ return module;
11028
+ });
11029
+ config.startMap = config.startMap || config.start?.map;
11030
+ return config;
11031
+ }
11032
+ //#endregion
11033
+ //#region src/compatibility-v4/index.ts
11034
+ var MODULE_NAME = "virtual:rpgjs-v4-modules";
11035
+ var CLIENT_CONFIG = "virtual:rpgjs-v4-client-config";
11036
+ var SERVER_ENTRY = "virtual:rpgjs-v4-server-entry";
11037
+ var CLIENT_ENTRY = "virtual:rpgjs-v4-client-entry";
11038
+ var STANDALONE_ENTRY = "virtual:rpgjs-v4-standalone-entry";
11039
+ var LEGACY_MOBILE_GUI = "virtual:rpgjs-v4-legacy-mobile-gui";
11040
+ var LEGACY_DEFAULT_GUI = "virtual:rpgjs-v4-legacy-default-gui";
11041
+ var LEGACY_GAMEPAD = "virtual:rpgjs-v4-legacy-gamepad";
11042
+ var LEGACY_MODULES = {
11043
+ "@rpgjs/mobile-gui": LEGACY_MOBILE_GUI,
11044
+ "@rpgjs/default-gui": LEGACY_DEFAULT_GUI,
11045
+ "@rpgjs/gamepad": LEGACY_GAMEPAD
11046
+ };
11047
+ var TILED_EXTENSIONS = [
11048
+ ".tmx",
11049
+ ".tsx",
11050
+ ".png",
11051
+ ".jpg",
11052
+ ".jpeg",
11053
+ ".gif",
11054
+ ".webp",
11055
+ ".svg"
11056
+ ];
11057
+ async function importWebSocketServer() {
11058
+ if (typeof process === "undefined" || !process.versions?.node) return null;
11059
+ try {
11060
+ const { createRequire } = await import("module");
11061
+ const ws = createRequire(import.meta.url)("ws");
11062
+ return ws.WebSocketServer || ws.default?.WebSocketServer || ws;
11063
+ } catch {
11064
+ return null;
11065
+ }
11066
+ }
11067
+ function verifyDefaultExport(importObject) {
11068
+ if (!importObject.variablesString) return "[]";
11069
+ return dedent`
11070
+ [${importObject.variablesString}].map((value) => {
11071
+ if (!value) throw new Error('Missing default export in ${importObject.relativePath}')
11072
+ return value
11073
+ })
11074
+ `;
11075
+ }
11076
+ function normalizeDatabase(variableList) {
11077
+ if (!variableList) return "{}";
11078
+ return dedent`
11079
+ Object.assign({}, ...[${variableList}].map((value) => {
11080
+ if (!value) return {}
11081
+ if (typeof value === 'function') {
11082
+ return { [value.id || value.name]: value }
11083
+ }
11084
+ return value
11085
+ }))
11086
+ `;
11087
+ }
11088
+ function stripQuery(url) {
11089
+ return url.split("?", 1)[0].split("#", 1)[0];
11090
+ }
11091
+ function normalizePublicBasePath(basePath = "map") {
11092
+ return basePath.trim().replace(/^\/+|\/+$/g, "") || "map";
11093
+ }
11094
+ function getMimeType(file) {
11095
+ switch (path.extname(file).toLowerCase()) {
11096
+ case ".tmx":
11097
+ case ".tsx":
11098
+ case ".world": return "application/xml";
11099
+ case ".png": return "image/png";
11100
+ case ".jpg":
11101
+ case ".jpeg": return "image/jpeg";
11102
+ case ".gif": return "image/gif";
11103
+ case ".webp": return "image/webp";
11104
+ case ".svg": return "image/svg+xml";
11105
+ default: return "application/octet-stream";
11106
+ }
11107
+ }
11108
+ function isTiledAsset(file) {
11109
+ return TILED_EXTENSIONS.includes(path.extname(file).toLowerCase());
11110
+ }
11111
+ function moduleRootPath(modulePath, projectRoot = process.cwd()) {
11112
+ return path.resolve(projectRoot, transformPathIfModule(modulePath));
11113
+ }
11114
+ function hasTiledAssets(directory) {
11115
+ return fs.existsSync(directory) && getAllFiles(directory).some(isTiledAsset);
11116
+ }
11117
+ function getTiledAssetRoots(modulePath, projectRoot = process.cwd()) {
11118
+ const moduleRoot = moduleRootPath(modulePath, projectRoot);
11119
+ const roots = [];
11120
+ const mapsRoot = path.join(moduleRoot, "maps");
11121
+ const worldsRoot = path.join(moduleRoot, "worlds");
11122
+ if (hasTiledAssets(mapsRoot)) roots.push({
11123
+ root: mapsRoot,
11124
+ moduleRoot
11125
+ });
11126
+ if (fs.existsSync(worldsRoot)) for (const dirent of fs.readdirSync(worldsRoot, { withFileTypes: true })) {
11127
+ if (!dirent.isDirectory()) continue;
11128
+ const root = path.join(worldsRoot, dirent.name);
11129
+ if (hasTiledAssets(root)) roots.push({
11130
+ root,
11131
+ moduleRoot
11132
+ });
11133
+ }
11134
+ return roots;
11135
+ }
11136
+ function getAllTiledAssetRoots(modules, projectRoot = process.cwd()) {
11137
+ return modules.filter((module) => module.startsWith(".")).flatMap((module) => getTiledAssetRoots(module, projectRoot));
11138
+ }
11139
+ function mapIdFromTmx(file, assetRoot) {
11140
+ return toPosix(path.relative(assetRoot, file)).replace(/\.tmx$/i, "");
11141
+ }
11142
+ function publicTiledFilePath(file, assetRoot, basePath = "map") {
11143
+ return `/${normalizePublicBasePath(basePath)}/${toPosix(path.relative(assetRoot, file))}`;
11144
+ }
11145
+ function hasSiblingMapScript(file) {
11146
+ return fs.existsSync(file.replace(/\.tmx$/i, ".ts"));
11147
+ }
11148
+ function createTiledMapEntries(modulePath, options, projectRoot = process.cwd()) {
11149
+ return getTiledAssetRoots(modulePath, projectRoot).flatMap(({ root }) => getAllFiles(root).filter((file) => file.toLowerCase().endsWith(".tmx")).filter((file) => !hasSiblingMapScript(file)).map((file) => {
11150
+ return `{ id: '${mapIdFromTmx(file, root)}', file: '${publicTiledFilePath(file, root, options.tiledMapBasePath)}' }`;
11151
+ })).join(",");
11152
+ }
11153
+ function mapIdFromWorldFileName(fileName) {
11154
+ const withoutExtension = toPosix(fileName).replace(/\.tmx$/i, "");
11155
+ return withoutExtension.split("/").filter(Boolean).at(-1) ?? withoutExtension;
11156
+ }
11157
+ function loadWorldFile(file) {
11158
+ const world = JSON.parse(fs.readFileSync(file, "utf8"));
11159
+ const maps = Array.isArray(world.maps) ? world.maps.map((map) => ({
11160
+ ...map,
11161
+ id: map.id ?? (map.fileName ? mapIdFromWorldFileName(map.fileName) : void 0),
11162
+ worldX: map.worldX ?? map.x ?? 0,
11163
+ worldY: map.worldY ?? map.y ?? 0,
11164
+ width: map.width ?? map.widthPx ?? 0,
11165
+ height: map.height ?? map.heightPx ?? 0
11166
+ })) : [];
11167
+ return {
11168
+ ...world,
11169
+ id: world.id ?? path.basename(file, ".world"),
11170
+ maps
11171
+ };
11172
+ }
11173
+ function createWorldMapEntries(modulePath, projectRoot = process.cwd()) {
11174
+ const worldsRoot = path.join(moduleRootPath(modulePath, projectRoot), "worlds");
11175
+ if (!fs.existsSync(worldsRoot)) return "";
11176
+ return getAllFiles(worldsRoot).filter((file) => file.endsWith(".world")).map((file) => JSON.stringify(loadWorldFile(file))).join(",");
11177
+ }
11178
+ function copyTiledAssets(assetRoots, outputDir, basePath = "map") {
11179
+ const publicBasePath = normalizePublicBasePath(basePath);
11180
+ const copied = /* @__PURE__ */ new Set();
11181
+ for (const { root } of assetRoots) for (const file of getAllFiles(root).filter(isTiledAsset)) {
11182
+ const relativePath = toPosix(path.relative(root, file));
11183
+ const target = path.join(outputDir, publicBasePath, relativePath);
11184
+ if (copied.has(target)) warn(`Tiled asset collision while copying ${relativePath}`);
11185
+ copied.add(target);
11186
+ fs.mkdirSync(path.dirname(target), { recursive: true });
11187
+ fs.copyFileSync(file, target);
11188
+ }
11189
+ }
11190
+ function serveTiledAssets(server, assetRoots, basePath = "map") {
11191
+ const publicBasePath = `/${normalizePublicBasePath(basePath)}`;
11192
+ server.middlewares.use((req, res, next) => {
11193
+ if (!req.url?.startsWith(publicBasePath)) return next();
11194
+ const requestPath = decodeURIComponent(stripQuery(req.url).slice(publicBasePath.length).replace(/^\/+/, ""));
11195
+ for (const { root } of assetRoots) {
11196
+ const file = path.resolve(root, requestPath);
11197
+ if (!file.startsWith(root + path.sep) && file !== root) continue;
11198
+ if (!fs.existsSync(file) || !fs.statSync(file).isFile() || !isTiledAsset(file)) continue;
11199
+ res.setHeader("Content-Type", getMimeType(file));
11200
+ res.setHeader("Cache-Control", "no-cache");
11201
+ res.end(fs.readFileSync(file));
11202
+ return;
11203
+ }
11204
+ res.statusCode = 404;
11205
+ res.end("Not Found");
11206
+ });
11207
+ }
11208
+ function loadServerModuleFiles(modulePath, options, config, projectRoot = process.cwd()) {
11209
+ const modulesCreated = options.modulesCreated ?? [];
11210
+ if (!modulesCreated.includes(modulePath)) modulesCreated.push(modulePath);
11211
+ const importPlayer = importString(modulePath, "player", "player", projectRoot);
11212
+ const importEngine = importString(modulePath, "server", "server", projectRoot);
11213
+ const mapStandaloneFilesString = searchFolderAndTransformToImportString("maps", modulePath, ".ts", void 0, void 0, projectRoot);
11214
+ const mapFilesString = createTiledMapEntries(modulePath, options, projectRoot);
11215
+ const worldFilesString = createWorldMapEntries(modulePath, projectRoot);
11216
+ const eventsFilesString = searchFolderAndTransformToImportString("events", modulePath, ".ts", void 0, void 0, projectRoot);
11217
+ const databaseFilesString = searchFolderAndTransformToImportString("database", modulePath, ".ts", void 0, void 0, projectRoot);
11218
+ const hasMaps = !!mapFilesString && !!mapStandaloneFilesString.variablesString;
11219
+ const hitbox = config.start?.hitbox;
11220
+ const startHook = modulesCreated.length === 1 && (config.start?.graphic || hitbox || config.startMap) ? dedent`
11221
+ const __rpgjsV4Player = player || {}
11222
+ const __rpgjsV4LastConnected = __rpgjsV4Player.onConnected
11223
+ __rpgjsV4Player.onConnected = async (player) => {
11224
+ if (__rpgjsV4LastConnected) await __rpgjsV4LastConnected(player)
11225
+ ${config.start?.graphic ? `player.setGraphic('${config.start.graphic}')` : ""}
11226
+ ${hitbox ? `player.setHitbox(${hitbox[0]}, ${hitbox[1]})` : ""}
11227
+ ${config.startMap ? `await player.changeMap('${config.startMap}')` : ""}
11228
+ }
11229
+ ` : "";
11230
+ return dedent`
11231
+ ${importPlayer || "const player = {}"}
11232
+ ${importEngine}
11233
+ ${mapStandaloneFilesString.importString}
11234
+ ${eventsFilesString.importString}
11235
+ ${databaseFilesString.importString}
11236
+
11237
+ ${startHook}
11238
+
11239
+ export default {
11240
+ player: ${startHook ? "__rpgjsV4Player" : "player"},
11241
+ ${importEngine ? "engine: server," : ""}
11242
+ events: ${verifyDefaultExport(eventsFilesString)},
11243
+ database: ${normalizeDatabase(databaseFilesString.variablesString)},
11244
+ maps: [${mapFilesString}${hasMaps ? "," : ""}${mapStandaloneFilesString.variablesString}],
11245
+ worldMaps: [${worldFilesString}]
11246
+ }
11247
+ `;
11248
+ }
11249
+ function createServerEntryLoad() {
11250
+ return dedent`
11251
+ import { createServer, provideServerModules } from '@rpgjs/server'
11252
+ import { provideTiledMap } from '@rpgjs/tiledmap/server'
11253
+ import modules from '${MODULE_NAME}'
11254
+
11255
+ export default createServer({
11256
+ providers: [
11257
+ provideServerModules(modules),
11258
+ provideTiledMap()
11259
+ ]
11260
+ })
11261
+ `;
11262
+ }
11263
+ function loadSpriteSheet(directoryName, modulePath, options, projectRoot = process.cwd(), warning = false) {
11264
+ const importSprites = searchFolderAndTransformToImportString(directoryName, modulePath, ".ts", void 0, void 0, projectRoot);
11265
+ let propImagesString = "";
11266
+ let variablesString = importSprites.variablesString;
11267
+ if (importSprites.importString) {
11268
+ const images = getAllFiles(importSprites.folder).filter((file) => {
11269
+ return [
11270
+ ".png",
11271
+ ".jpg",
11272
+ ".jpeg",
11273
+ ".gif",
11274
+ ".bmp",
11275
+ ".webp",
11276
+ ".svg"
11277
+ ].some((ext) => file.toLowerCase().endsWith(ext));
11278
+ });
11279
+ if (!images.length) warn(`No spritesheet image found in ${directoryName}`);
11280
+ else {
11281
+ const imageImports = images.map((file) => {
11282
+ const filename = path.basename(file);
11283
+ const basename = filename.replace(path.extname(filename), "");
11284
+ const importPath = `${importPathForFile(file, projectRoot)}?url`;
11285
+ return {
11286
+ basename,
11287
+ importPath,
11288
+ variableName: formatVariableName(importPath)
11289
+ };
11290
+ });
11291
+ const objectString = imageImports.map(({ basename, variableName }) => `"${basename}": ${variableName}`).join(",\n");
11292
+ const imageImportString = imageImports.map(({ importPath, variableName }) => `import ${variableName} from '${importPath}'`).join("\n");
11293
+ const generatedSpritesheetsVariable = `__rpgjsV4Spritesheets_${formatVariableName(directoryName)}`;
11294
+ variablesString = `...${generatedSpritesheetsVariable}`;
11295
+ const dimensions = sizeOf(fs.readFileSync(images.at(-1)));
11296
+ propImagesString = dedent`
11297
+ ${imageImportString}
11298
+ const ${generatedSpritesheetsVariable} = [${importSprites.variablesString}].flatMap((spritesheet) => {
11299
+ return Object.entries({ ${objectString} }).map(([id, image]) => ({
11300
+ ...spritesheet,
11301
+ ...(spritesheet.prototype ?? {}),
11302
+ id,
11303
+ image,
11304
+ }))
11305
+ })
11306
+ ;[${importSprites.variablesString}].forEach((spritesheet) => {
11307
+ spritesheet.images = { ${objectString} }
11308
+ spritesheet.prototype ||= {}
11309
+ spritesheet.prototype.width = ${dimensions.width ?? 0}
11310
+ spritesheet.prototype.height = ${dimensions.height ?? 0}
11311
+ })
11312
+ ${generatedSpritesheetsVariable}.forEach((spritesheet) => {
11313
+ spritesheet.width = ${dimensions.width ?? 0}
11314
+ spritesheet.height = ${dimensions.height ?? 0}
11315
+ })
11316
+ `;
11317
+ }
11318
+ } else if (warning) warn(`No spritesheet folder found in ${directoryName}`);
11319
+ return {
11320
+ ...importSprites,
11321
+ variablesString,
11322
+ propImagesString
11323
+ };
11324
+ }
11325
+ function loadClientFiles(modulePath, options, config, projectRoot = process.cwd()) {
11326
+ const importSpriteString = importString(modulePath, "sprite", "sprite", projectRoot);
11327
+ const importSceneMapString = importString(modulePath, "scene-map", "sceneMap", projectRoot);
11328
+ const importEngine = importString(modulePath, "client", "engine", projectRoot);
11329
+ const guiFilesString = searchFolderAndTransformToImportString("gui", modulePath, [
11330
+ ".vue",
11331
+ ".tsx",
11332
+ ".jsx",
11333
+ ".ce"
11334
+ ], void 0, void 0, projectRoot);
11335
+ const soundStandaloneFilesString = searchFolderAndTransformToImportString("sounds", modulePath, ".ts", void 0, void 0, projectRoot);
11336
+ const soundFilesString = searchFolderAndTransformToImportString("sounds", modulePath, [".mp3", ".ogg"], void 0, { customFilter: (file) => !fs.existsSync(file.replace(/\.(mp3|ogg)$/, ".ts")) }, projectRoot);
11337
+ const spritesheets = [];
11338
+ for (const directory of config.spritesheetDirectories ?? []) spritesheets.push(loadSpriteSheet(directory, modulePath, options, projectRoot));
11339
+ if (!(config.spritesheetDirectories ?? []).includes("characters")) spritesheets.push(loadSpriteSheet("characters", modulePath, options, projectRoot));
11340
+ const spritesheetRoot = path.resolve(projectRoot, transformPathIfModule(modulePath), "spritesheets");
11341
+ if (fs.existsSync(spritesheetRoot)) {
11342
+ for (const dirent of fs.readdirSync(spritesheetRoot, { withFileTypes: true })) if (dirent.isDirectory()) spritesheets.push(loadSpriteSheet(path.join("spritesheets", dirent.name), modulePath, options, projectRoot, true));
11343
+ }
11344
+ const activeSpritesheets = spritesheets.filter((spritesheet) => spritesheet.importString);
11345
+ const hasSounds = !!soundFilesString.variablesString && !!soundStandaloneFilesString.variablesString;
11346
+ return dedent`
11347
+ ${importSpriteString || "const sprite = {}"}
11348
+ ${importSceneMapString}
11349
+ ${importEngine}
11350
+ ${activeSpritesheets.map((spritesheet) => spritesheet.importString).join("\n")}
11351
+ ${guiFilesString.importString}
11352
+ ${soundFilesString.importString}
11353
+ ${soundStandaloneFilesString.importString}
11354
+
11355
+ ${activeSpritesheets.map((spritesheet) => spritesheet.propImagesString).join("\n")}
11356
+
11357
+ export default {
11358
+ spritesheets: [${activeSpritesheets.map((spritesheet) => spritesheet.variablesString).join(",")}],
11359
+ sprite,
11360
+ ${importEngine ? "engine," : ""}
11361
+ sceneMap: ${importSceneMapString ? "sceneMap" : "{}"},
11362
+ gui: [${guiFilesString.variablesString}],
11363
+ sounds: [${soundFilesString.variablesString}${hasSounds ? "," : ""}${soundStandaloneFilesString.variablesString}]
11364
+ }
11365
+ `;
11366
+ }
11367
+ function createModuleLoad(id, variableName, modulePath, options, config, projectRoot = process.cwd()) {
11368
+ if (modulePath === LEGACY_MOBILE_GUI) return dedent`
11369
+ import { withMobile } from '@rpgjs/client'
11370
+ export default { client: withMobile(), server: {} }
11371
+ `;
11372
+ if (modulePath === LEGACY_DEFAULT_GUI || modulePath === LEGACY_GAMEPAD) return "export default { client: {}, server: {} }";
11373
+ const clientFile = `virtual:${variableName}-client`;
11374
+ const serverFile = `virtual:${variableName}-server`;
11375
+ if (id.startsWith(`${serverFile}?server`)) return loadServerModuleFiles(modulePath, options, config, projectRoot);
11376
+ if (id.startsWith(`${clientFile}?client`)) return loadClientFiles(modulePath, options, config, projectRoot);
11377
+ const modulePathId = path.resolve(projectRoot, transformPathIfModule(modulePath));
11378
+ const packageJson = path.join(modulePathId, "package.json");
11379
+ const indexFile = path.join(modulePathId, "index.ts");
11380
+ if (fs.existsSync(packageJson)) {
11381
+ const { main: entryPoint } = JSON.parse(fs.readFileSync(packageJson, "utf8"));
11382
+ if (entryPoint) {
11383
+ const entryFile = path.join(modulePathId, entryPoint);
11384
+ return dedent`
11385
+ import mod from '${modulePath.startsWith(".") ? importPathForFile(entryFile, projectRoot) : resolveModuleImport(toPosix(path.join(modulePath, entryPoint)))}'
11386
+ export default mod
11387
+ `;
11388
+ }
11389
+ } else if (fs.existsSync(indexFile)) return dedent`
11390
+ import mod from '${importPathForFile(indexFile, projectRoot)}'
11391
+ export default mod
11392
+ `;
11393
+ return dedent`
11394
+ import client from 'client!${clientFile}'
11395
+ import server from 'server!${serverFile}'
11396
+ export default { client, server }
11397
+ `;
11398
+ }
11399
+ function createModulesLoad(modules) {
11400
+ const modulesToImport = modules.reduce((acc, module) => {
11401
+ const resolvedModule = LEGACY_MODULES[module] ?? module;
11402
+ acc[formatVariableName(resolvedModule)] = resolvedModule;
11403
+ return acc;
11404
+ }, {});
11405
+ return dedent`
11406
+ ${Object.entries(modulesToImport).map(([variableName, module]) => `import ${variableName} from '${resolveModuleImport(module)}'`).join("\n")}
11407
+ export default [${Object.keys(modulesToImport).join(",")}]
11408
+ `;
11409
+ }
11410
+ function createClientConfigLoad(config, options = {}) {
11411
+ return dedent`
11412
+ import { provideClientGlobalConfig, provideClientModules } from '@rpgjs/client'
11413
+ import { provideTiledMap } from '@rpgjs/tiledmap/client'
11414
+ import modules from '${MODULE_NAME}'
11415
+ export default {
11416
+ providers: [
11417
+ provideTiledMap({ basePath: '${normalizePublicBasePath(options.tiledMapBasePath)}' }),
11418
+ provideClientGlobalConfig(${JSON.stringify(config)}),
11419
+ provideClientModules(modules)
11420
+ ]
11421
+ }
11422
+ `;
11423
+ }
11424
+ function createStandaloneEntryLoad() {
11425
+ return dedent`
11426
+ import { mergeConfig } from '@signe/di'
11427
+ import { provideRpg, startGame } from '@rpgjs/client'
11428
+ import server from '${SERVER_ENTRY}'
11429
+ import configClient from '${CLIENT_CONFIG}'
11430
+
11431
+ startGame(mergeConfig(configClient, {
11432
+ providers: [provideRpg(server)]
11433
+ }))
11434
+ `;
11435
+ }
11436
+ function createClientEntryLoad() {
11437
+ return dedent`
11438
+ import { mergeConfig } from '@signe/di'
11439
+ import { provideMmorpg, startGame } from '@rpgjs/client'
11440
+ import configClient from '${CLIENT_CONFIG}'
11441
+
11442
+ startGame(mergeConfig(configClient, {
11443
+ providers: [provideMmorpg({})]
11444
+ }))
11445
+ `;
11446
+ }
11447
+ function normalizeModules(config) {
11448
+ return config.modules?.length ? config.modules : ["./src/modules/main"];
11449
+ }
11450
+ function normalizeAliases(aliases = {}) {
11451
+ return Object.fromEntries(Object.entries(aliases).map(([key, value]) => [key, value.startsWith(".") ? path.resolve(process.cwd(), value) : value]));
11452
+ }
11453
+ function compatibilityV4Plugin(options = {}) {
11454
+ let viteMode = "development";
11455
+ let config = loadConfigFileSync(viteMode);
11456
+ let modules = normalizeModules(config);
11457
+ let modulesCreated = [];
11458
+ let viteRoot = process.cwd();
11459
+ let viteOutputDir = path.resolve(viteRoot, config.compilerOptions?.build?.outputDir ?? "dist");
11460
+ let resolvedOptions = {
11461
+ type: options.type ?? config.type ?? process.env.RPG_TYPE ?? "rpg",
11462
+ tiledMapBasePath: normalizePublicBasePath(options.tiledMapBasePath),
11463
+ serveMode: true,
11464
+ side: options.side ?? "client",
11465
+ ...options,
11466
+ config
11467
+ };
11468
+ let wsServer = null;
11469
+ const flagOptions = {
11470
+ side: resolvedOptions.side,
11471
+ mode: viteMode,
11472
+ type: resolvedOptions.type
11473
+ };
11474
+ return [
11475
+ flagTransform(flagOptions),
11476
+ vitePluginRequire(),
11477
+ {
11478
+ name: "rpgjs-v4-compatibility",
11479
+ enforce: "pre",
11480
+ config() {
11481
+ return {
11482
+ resolve: {
11483
+ alias: {
11484
+ "@": path.resolve(process.cwd(), "src"),
11485
+ ...normalizeAliases(config.compilerOptions?.alias)
11486
+ },
11487
+ extensions: [
11488
+ ".ts",
11489
+ ".js",
11490
+ ".jsx",
11491
+ ".json",
11492
+ ".vue",
11493
+ ".css",
11494
+ ".scss",
11495
+ ".sass",
11496
+ ".html",
11497
+ ".tmx",
11498
+ ".tsx",
11499
+ ".toml",
11500
+ ".ce"
11501
+ ]
11502
+ },
11503
+ assetsInclude: [
11504
+ "**/*.tmx",
11505
+ "**/*.world",
11506
+ "{!(gui)/**/*}.tsx"
11507
+ ]
11508
+ };
11509
+ },
11510
+ configResolved(viteConfig) {
11511
+ viteMode = viteConfig.mode || "development";
11512
+ viteRoot = viteConfig.root;
11513
+ viteOutputDir = path.resolve(viteRoot, viteConfig.build.outDir || "dist");
11514
+ config = loadConfigFileSync(viteMode, viteConfig.root);
11515
+ modules = normalizeModules(config);
11516
+ modulesCreated = [];
11517
+ resolvedOptions = {
11518
+ ...resolvedOptions,
11519
+ type: options.type ?? config.type ?? process.env.RPG_TYPE ?? "rpg",
11520
+ serveMode: viteConfig.command === "serve",
11521
+ mode: viteMode,
11522
+ config
11523
+ };
11524
+ flagOptions.side = resolvedOptions.side;
11525
+ flagOptions.mode = viteMode;
11526
+ flagOptions.type = resolvedOptions.type;
11527
+ },
11528
+ transformIndexHtml: {
11529
+ order: "pre",
11530
+ handler(html) {
11531
+ const script = `<script type="module">\nimport '${resolvedOptions.type === "mmorpg" ? CLIENT_ENTRY : STANDALONE_ENTRY}'\n<\/script>`;
11532
+ if (html.includes("<script type=\"module\"")) return html.replace(/<script\s+type="module"\s+src="[^"]*"[^>]*><\/script>/gi, script);
11533
+ return html.replace(/<\/head>/i, ` ${script}\n </head>`);
11534
+ }
11535
+ },
11536
+ handleHotUpdate() {
11537
+ modulesCreated = [];
11538
+ },
11539
+ resolveId(source) {
11540
+ if ([
11541
+ MODULE_NAME,
11542
+ CLIENT_CONFIG,
11543
+ SERVER_ENTRY,
11544
+ CLIENT_ENTRY,
11545
+ STANDALONE_ENTRY,
11546
+ ...Object.values(LEGACY_MODULES)
11547
+ ].includes(source)) return source;
11548
+ for (const module of modules) {
11549
+ const moduleName = resolveModuleImport(module);
11550
+ const variableName = formatVariableName(moduleName);
11551
+ if (source === moduleName || source === `virtual:${variableName}-client` || source === `virtual:${variableName}-server`) return source;
11552
+ }
11553
+ return null;
11554
+ },
11555
+ load(id) {
11556
+ if (id === MODULE_NAME) return createModulesLoad(modules);
11557
+ if (id === CLIENT_CONFIG) return createClientConfigLoad(config, resolvedOptions);
11558
+ if (id === SERVER_ENTRY) return createServerEntryLoad();
11559
+ if (id === CLIENT_ENTRY) return createClientEntryLoad();
11560
+ if (id === STANDALONE_ENTRY) return createStandaloneEntryLoad();
11561
+ if (Object.values(LEGACY_MODULES).includes(id)) return createModuleLoad(id, formatVariableName(id), id, {
11562
+ ...resolvedOptions,
11563
+ modulesCreated
11564
+ }, config);
11565
+ for (const module of modules) {
11566
+ const moduleName = resolveModuleImport(module);
11567
+ const variableName = formatVariableName(moduleName);
11568
+ if (id === moduleName || id.startsWith(`virtual:${variableName}-client?client`) || id.startsWith(`virtual:${variableName}-server?server`)) return createModuleLoad(id, variableName, module, {
11569
+ ...resolvedOptions,
11570
+ modulesCreated
11571
+ }, config);
11572
+ }
11573
+ return null;
11574
+ },
11575
+ async configureServer(server) {
11576
+ serveTiledAssets(server, getAllTiledAssetRoots(modules, server.config.root), resolvedOptions.tiledMapBasePath);
11577
+ if (resolvedOptions.type !== "mmorpg") return;
11578
+ const { default: serverModule } = await server.ssrLoadModule(SERVER_ENTRY);
11579
+ const transport = createRpgServerTransport(serverModule);
11580
+ const WebSocketServerClass = await importWebSocketServer();
11581
+ if (WebSocketServerClass) wsServer = new WebSocketServerClass({ noServer: true });
11582
+ logNetworkSimulationStatus();
11583
+ server.middlewares.use("/parties", async (req, res, next) => {
11584
+ await transport.handleNodeRequest(req, res, next, { mountedPath: "/parties" });
11585
+ });
11586
+ if (wsServer) server.httpServer?.on("upgrade", (request, socket, head) => {
11587
+ transport.handleUpgrade(wsServer, request, socket, head);
11588
+ });
11589
+ },
11590
+ buildEnd() {
11591
+ wsServer?.close();
11592
+ },
11593
+ generateBundle() {
11594
+ copyTiledAssets(getAllTiledAssetRoots(modules, viteRoot), viteOutputDir, resolvedOptions.tiledMapBasePath);
11595
+ }
11596
+ }
11597
+ ];
11598
+ }
11599
+ //#endregion
11600
+ export { compatibilityV4Plugin, directivePlugin, entryPointPlugin, removeImportsPlugin, replaceConfigImport, rpgjs, rpgjsModuleViteConfig, serverPlugin, tiledMapFolderPlugin };
10436
11601
 
10437
11602
  //# sourceMappingURL=index.js.map