@rpgjs/vite 5.0.0-beta.7 → 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 +23 -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 +1345 -166
  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-5HOX9Ovu.js
6812
+ //#region ../server/dist/module-CW79w6Lk.js
6805
6813
  /******************************************************************************
6806
6814
  Copyright (c) Microsoft Corporation.
6807
6815
 
@@ -8027,6 +8035,8 @@ function map(project, thisArg) {
8027
8035
  }));
8028
8036
  });
8029
8037
  }
8038
+ Array.isArray;
8039
+ Array.isArray;
8030
8040
  function mergeInternals(source, subscriber, project, concurrent, onBeforeNext, expand, innerSubScheduler, additionalFinalizer) {
8031
8041
  var buffer = [];
8032
8042
  var active = 0;
@@ -8277,8 +8287,8 @@ var getGlobalReactiveStore = () => {
8277
8287
  }
8278
8288
  let globalObj;
8279
8289
  if (typeof window !== "undefined") globalObj = window;
8290
+ else if (typeof process !== "undefined" && process.versions && process.versions.node) globalObj = Function("return this")();
8280
8291
  else if (typeof self !== "undefined") globalObj = self;
8281
- else if (typeof Function !== "undefined") globalObj = Function("return this")();
8282
8292
  else {
8283
8293
  console.warn("Unable to find global object, using local instance");
8284
8294
  return {
@@ -8367,6 +8377,7 @@ var createSyncClass = (currentClass, parentKey = null, parentClass = null, path
8367
8377
  currentClass.$path = path;
8368
8378
  if (parentClass) currentClass.$valuesChanges = parentClass.$valuesChanges;
8369
8379
  if (parentKey) setMetadata(currentClass, "id", parentKey);
8380
+ applyDecoratedSignalMetadata(currentClass);
8370
8381
  if (currentClass.$snapshot) for (const key of currentClass.$snapshot.keys()) {
8371
8382
  const signal = currentClass.$snapshot.get(key);
8372
8383
  const syncToClient = signal.options?.syncToClient ?? true;
@@ -8389,8 +8400,20 @@ var createSyncClass = (currentClass, parentKey = null, parentClass = null, path
8389
8400
  });
8390
8401
  }
8391
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
+ }
8392
8415
  var type = (_signal, path, options = {}, currentInstance) => {
8393
- const { syncToClient = true, persist: persist2 = true, transform } = options;
8416
+ const { syncToClient = true, persist: persist2 = true, transform, skipInitialSync = false } = options;
8394
8417
  let init = true;
8395
8418
  const handleObjectSubject = (value, propPath) => {
8396
8419
  const newPath = `${propPath}${value.key ? `.${value.key}` : ""}`;
@@ -8422,14 +8445,15 @@ var type = (_signal, path, options = {}, currentInstance) => {
8422
8445
  const savePath = (propPath, value) => {
8423
8446
  const transformedValue = transform && value !== "$delete" ? transform(value) : value;
8424
8447
  if (syncToClient) currentInstance.$valuesChanges.set(propPath, transformedValue);
8425
- 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);
8426
8449
  };
8427
8450
  const setupSubscription = (signal, signalPath) => {
8428
8451
  if (!isSignal(signal)) return;
8429
- if (syncToClient && currentInstance.$valuesChanges) {
8452
+ if (syncToClient && !skipInitialSync && currentInstance.$valuesChanges) {
8430
8453
  const initialValue = signal();
8431
8454
  const transformedInitialValue = transform ? transform(initialValue) : initialValue;
8432
- currentInstance.$valuesChanges.set(signalPath, transformedInitialValue);
8455
+ const initialPath = currentInstance.$path !== void 0 ? `${currentInstance.$path ? `${currentInstance.$path}.` : ""}${signalPath}` : signalPath;
8456
+ currentInstance.$valuesChanges.set(initialPath, transformedInitialValue);
8433
8457
  }
8434
8458
  signal.options = options;
8435
8459
  signal.observable.subscribe((value) => {
@@ -8461,7 +8485,7 @@ var type = (_signal, path, options = {}, currentInstance) => {
8461
8485
  init = false;
8462
8486
  return _signal;
8463
8487
  };
8464
- function sync(options) {
8488
+ function normalizeSyncOptions(options) {
8465
8489
  let classType;
8466
8490
  let persist2 = true;
8467
8491
  let syncToClient = true;
@@ -8473,18 +8497,27 @@ function sync(options) {
8473
8497
  if (options.hasOwnProperty("syncToClient")) syncToClient = options.syncToClient;
8474
8498
  if (options.hasOwnProperty("transform")) transform = options.transform;
8475
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);
8476
8513
  return function(target, propertyKey) {
8514
+ setSyncMetadata(target, propertyKey, normalizedOptions);
8477
8515
  const privatePropertyKey = `__${propertyKey}`;
8478
8516
  const getter = function() {
8479
8517
  return this[privatePropertyKey];
8480
8518
  };
8481
8519
  const setter = function(newVal) {
8482
- this[privatePropertyKey] = type(newVal, propertyKey, {
8483
- classType,
8484
- persist: persist2,
8485
- syncToClient,
8486
- transform
8487
- }, this);
8520
+ this[privatePropertyKey] = type(newVal, propertyKey, normalizedOptions, this);
8488
8521
  };
8489
8522
  Object.defineProperty(target, propertyKey, {
8490
8523
  get: getter,
@@ -8584,13 +8617,6 @@ var toCloneableSyncValue = (value, seen = /* @__PURE__ */ new WeakSet()) => {
8584
8617
  }
8585
8618
  return output;
8586
8619
  };
8587
- var Direction = /* @__PURE__ */ function(Direction) {
8588
- Direction["Up"] = "up";
8589
- Direction["Down"] = "down";
8590
- Direction["Left"] = "left";
8591
- Direction["Right"] = "right";
8592
- return Direction;
8593
- }({});
8594
8620
  var RpgCommonPlayer = class {
8595
8621
  constructor() {
8596
8622
  this.name = signal("");
@@ -8599,7 +8625,7 @@ var RpgCommonPlayer = class {
8599
8625
  this.y = signal(0);
8600
8626
  this.z = signal(0);
8601
8627
  this.tint = signal("white");
8602
- this.direction = signal(Direction.Down);
8628
+ this.direction = signal("down");
8603
8629
  this.speed = signal(4);
8604
8630
  this.graphics = signal([]);
8605
8631
  this.canMove = signal(true);
@@ -8748,6 +8774,9 @@ var RpgCommonPlayer = class {
8748
8774
  getDirection() {
8749
8775
  return this.direction();
8750
8776
  }
8777
+ isEvent() {
8778
+ return false;
8779
+ }
8751
8780
  };
8752
8781
  __decorate([id(), __decorateMetadata("design:type", String)], RpgCommonPlayer.prototype, "id", void 0);
8753
8782
  __decorate([sync(), __decorateMetadata("design:type", Object)], RpgCommonPlayer.prototype, "name", void 0);
@@ -9340,7 +9369,6 @@ var Context = class {
9340
9369
  return this.values[key];
9341
9370
  }
9342
9371
  };
9343
- function setInject(_context) {}
9344
9372
  var context = new Context();
9345
9373
  context["side"] = "server";
9346
9374
  var MAP_UPDATE_TOKEN_HEADER = "x-rpgjs-map-update-token";
@@ -9422,8 +9450,17 @@ function getTiledBasePaths(paths) {
9422
9450
  function resolveMapUpdateToken(explicitToken) {
9423
9451
  return explicitToken ?? readEnvVariable$1("RPGJS_MAP_UPDATE_TOKEN") ?? "";
9424
9452
  }
9453
+ function normalizeMapUpdateHeaders(init) {
9454
+ if (!init) return void 0;
9455
+ if (init instanceof Headers || Array.isArray(init)) return init;
9456
+ if (init instanceof Map) return Array.from(init.entries()).filter((entry) => entry[1] !== void 0);
9457
+ return Object.entries(init).flatMap(([key, value]) => {
9458
+ if (value === void 0) return [];
9459
+ return Array.isArray(value) ? value.map((item) => [key, item]) : [[key, value]];
9460
+ });
9461
+ }
9425
9462
  function createMapUpdateHeaders(token, init) {
9426
- const headers = new Headers(init);
9463
+ const headers = new Headers(normalizeMapUpdateHeaders(init));
9427
9464
  if (!headers.has("content-type")) headers.set("content-type", "application/json");
9428
9465
  const resolvedToken = resolveMapUpdateToken(token);
9429
9466
  if (resolvedToken) headers.set(MAP_UPDATE_TOKEN_HEADER, resolvedToken);
@@ -9839,50 +9876,457 @@ function logNetworkSimulationStatus() {
9839
9876
  console.log(`\x1b[36m[NETWORK SIMULATION]\x1b[0m Latency simulation: ${latencyStatus.ms}ms ping${filterInfo}`);
9840
9877
  } else console.log("\x1B[36m[NETWORK SIMULATION]\x1B[0m Latency simulation: disabled");
9841
9878
  }
9842
- var PartyRoom = class {
9843
- constructor(id) {
9844
- this.env = {};
9845
- 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) {
9846
10098
  this.connections = /* @__PURE__ */ new Map();
9847
- this.storageData = /* @__PURE__ */ new Map();
9848
- this.id = id;
9849
- 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
+ };
9850
10117
  }
9851
- async broadcast(message, except = []) {
9852
- const data = typeof message === "string" ? message : JSON.stringify(message);
9853
- const sendPromises = [];
9854
- for (const [connectionId, connection] of this.connections) if (!except.includes(connectionId)) sendPromises.push(connection.send(data));
9855
- await Promise.all(sendPromises);
10118
+ blockConcurrencyWhile(callback) {
10119
+ return callback();
10120
+ }
10121
+ broadcast(msg, without = []) {
10122
+ for (const connection of this.connections.values()) if (!without.includes(connection.id)) connection.send(msg);
9856
10123
  }
9857
10124
  getConnection(id) {
9858
- 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;
9859
10128
  }
9860
- getConnections(tag) {
9861
- return this.connections.values();
10129
+ getConnections() {
10130
+ return Array.from(this.connections.values());
9862
10131
  }
9863
10132
  addConnection(connection) {
9864
10133
  this.connections.set(connection.id, connection);
9865
10134
  }
9866
- removeConnection(connectionId) {
9867
- 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);
9868
10141
  }
9869
- get storage() {
9870
- return {
9871
- put: async (key, value) => {
9872
- this.storageData.set(key, value);
9873
- },
9874
- get: async (key) => {
9875
- return this.storageData.get(key);
9876
- },
9877
- delete: async (key) => {
9878
- this.storageData.delete(key);
9879
- },
9880
- list: async () => {
9881
- return Array.from(this.storageData.entries());
9882
- }
9883
- };
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;
10168
+ }
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);
9884
10256
  }
9885
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
+ }
9886
10330
  function normalizePathPrefix(path, fallback) {
9887
10331
  const trimmed = (path || fallback).trim();
9888
10332
  if (!trimmed) return fallback;
@@ -9987,25 +10431,12 @@ function resolveUrlFromSocketRequest(request) {
9987
10431
  url
9988
10432
  };
9989
10433
  }
9990
- function createConnectionContext(url, headers, method) {
9991
- const normalizedHeaders = /* @__PURE__ */ new Map();
9992
- headers.forEach((value, key) => {
9993
- normalizedHeaders.set(key.toLowerCase(), value);
9994
- });
9995
- return {
9996
- request: {
9997
- headers: {
9998
- has: (name) => normalizedHeaders.has(name.toLowerCase()),
9999
- get: (name) => normalizedHeaders.get(name.toLowerCase()),
10000
- entries: () => normalizedHeaders.entries(),
10001
- keys: () => normalizedHeaders.keys(),
10002
- values: () => normalizedHeaders.values()
10003
- },
10004
- method,
10005
- url: url.toString()
10006
- },
10007
- url
10008
- };
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;
10009
10440
  }
10010
10441
  var RpgServerTransport = class {
10011
10442
  constructor(serverModule, options = {}) {
@@ -10018,10 +10449,33 @@ var RpgServerTransport = class {
10018
10449
  this.mapUpdateToken = resolveMapUpdateToken(options.mapUpdateToken);
10019
10450
  this.partiesPath = normalizePathPrefix(options.partiesPath || "/parties/main", "/parties/main");
10020
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
+ });
10021
10476
  }
10022
10477
  async ensureServerContext() {
10023
10478
  if (this.serverContextInitialized) return;
10024
- setInject(context);
10025
10479
  await injector(context, [provideServerModules([])]);
10026
10480
  this.serverContextInitialized = true;
10027
10481
  }
@@ -10033,52 +10487,17 @@ var RpgServerTransport = class {
10033
10487
  }
10034
10488
  async ensureRoomAndServer(roomId, host) {
10035
10489
  if (host) this.lastKnownHost = host;
10036
- let room = this.rooms.get(roomId);
10037
- if (!room) {
10038
- room = new PartyRoom(roomId);
10039
- this.rooms.set(roomId, room);
10040
- console.log(`Created new room: ${roomId}`);
10041
- }
10042
- let rpgServer = this.servers.get(roomId);
10043
- if (!rpgServer) {
10044
- await this.ensureServerContext();
10045
- rpgServer = new this.serverModule(room);
10046
- this.servers.set(roomId, rpgServer);
10047
- console.log(`Created new server instance for room: ${roomId}`);
10048
- if (typeof rpgServer.onStart === "function") try {
10049
- await rpgServer.onStart();
10050
- console.log(`Server started for room: ${roomId}`);
10051
- } catch (error) {
10052
- console.error(`Error starting server for room ${roomId}:`, error);
10053
- }
10054
- if (this.initializeMaps) await updateMap(roomId, rpgServer, {
10055
- host: host || this.lastKnownHost,
10056
- mapUpdateToken: this.mapUpdateToken,
10057
- tiledBasePaths: this.tiledBasePaths
10058
- });
10059
- }
10060
- 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}`);
10061
10494
  return {
10062
10495
  room,
10063
10496
  rpgServer
10064
10497
  };
10065
10498
  }
10066
- buildPartiesContext() {
10067
- return { main: { get: async (targetRoomId) => {
10068
- return { fetch: async (path, init) => {
10069
- const method = (init?.method || "GET").toUpperCase();
10070
- const headers = toHeaders(init?.headers);
10071
- const requestPath = path.startsWith("/") ? path : `/${path}`;
10072
- let bodyText = "";
10073
- if (typeof init?.body === "string") bodyText = init.body;
10074
- else if (typeof init?.body !== "undefined") bodyText = JSON.stringify(init.body);
10075
- return this.dispatchRoomRequest(targetRoomId, createRequestLike(`http://localhost${this.partiesPath}/${targetRoomId}${requestPath}`, method, headers, bodyText), this.lastKnownHost);
10076
- } };
10077
- } } };
10078
- }
10079
10499
  async dispatchRoomRequest(roomId, requestLike, host) {
10080
- const { room, rpgServer } = await this.ensureRoomAndServer(roomId, host);
10081
- room.context.parties = this.buildPartiesContext();
10500
+ const { rpgServer } = await this.ensureRoomAndServer(roomId, host);
10082
10501
  return normalizeEngineResponse(await rpgServer.onRequest?.(requestLike));
10083
10502
  }
10084
10503
  async fetch(request, init) {
@@ -10111,8 +10530,7 @@ var RpgServerTransport = class {
10111
10530
  next?.();
10112
10531
  return false;
10113
10532
  }
10114
- const bodyText = await readNodeBody(req);
10115
- 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));
10116
10534
  return true;
10117
10535
  } catch (error) {
10118
10536
  console.error("Error handling RPG-JS request:", error);
@@ -10130,51 +10548,17 @@ var RpgServerTransport = class {
10130
10548
  console.log(`WebSocket upgrade request: ${normalizedRequest.url.pathname}`);
10131
10549
  const queryParams = Object.fromEntries(normalizedRequest.url.searchParams.entries());
10132
10550
  console.log(`Room: ${route.roomId}, Query params:`, queryParams);
10133
- const { room, rpgServer } = await this.ensureRoomAndServer(route.roomId, normalizedRequest.url.host);
10134
- room.context.parties = this.buildPartiesContext();
10135
- const connection = new PartyConnection(ws, queryParams._pk, normalizedRequest.rawUrl);
10136
- room.addConnection(connection);
10137
- console.log(`WebSocket connection established: ${connection.id} in room: ${route.roomId}`);
10138
- let isClosed = false;
10139
- const cleanup = async (logMessage, error) => {
10140
- if (isClosed) return;
10141
- isClosed = true;
10142
- if (logMessage) console.log(logMessage);
10143
- if (error) console.error("WebSocket error:", error);
10144
- room.removeConnection(connection.id);
10145
- await rpgServer.onClose?.(connection);
10146
- };
10147
- ws.on("message", async (data) => {
10148
- try {
10149
- const rawMessage = typeof data === "string" ? data : data.toString();
10150
- if (PartyConnection.packetLossEnabled && PartyConnection.packetLossRate > 0) {
10151
- if (!PartyConnection.packetLossFilter || rawMessage.includes(PartyConnection.packetLossFilter)) {
10152
- if (Math.random() < PartyConnection.packetLossRate) {
10153
- console.log(`\x1b[31m[PACKET LOSS]\x1b[0m Connection ${connection.id}: Server dropped an incoming packet (${(PartyConnection.packetLossRate * 100).toFixed(1)}% loss rate)`);
10154
- console.log(`\x1b[33m[PACKET DATA]\x1b[0m ${rawMessage.slice(0, 100)}${rawMessage.length > 100 ? "..." : ""}`);
10155
- return;
10156
- }
10157
- }
10158
- }
10159
- connection.bufferIncoming(rawMessage, async (batch) => {
10160
- for (const message of batch) await rpgServer.onMessage?.(message, connection);
10161
- });
10162
- } catch (error) {
10163
- console.error("Error processing WebSocket message:", error);
10164
- }
10165
- });
10166
- ws.on("close", () => {
10167
- cleanup(`WebSocket connection closed: ${connection.id} from room: ${route.roomId}`);
10168
- });
10169
- ws.on("error", (error) => {
10170
- cleanup(void 0, error);
10171
- });
10172
- if (typeof rpgServer.onConnect === "function") await rpgServer.onConnect(connection, createConnectionContext(normalizedRequest.url, normalizedRequest.headers, normalizedRequest.method));
10173
- 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({
10174
10558
  type: "connected",
10175
10559
  id: connection.id,
10176
10560
  message: "Connected to RPG-JS server"
10177
- });
10561
+ }));
10178
10562
  return true;
10179
10563
  } catch (error) {
10180
10564
  console.error("Error establishing WebSocket connection:", error);
@@ -10184,7 +10568,11 @@ var RpgServerTransport = class {
10184
10568
  }
10185
10569
  async handleUpgrade(wsServer, request, socket, head) {
10186
10570
  const host = toHeaders(request.headers).get("host") || "localhost";
10187
- 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;
10188
10576
  wsServer.handleUpgrade(request, socket, head, (ws) => {
10189
10577
  this.acceptWebSocket(ws, request);
10190
10578
  });
@@ -10196,7 +10584,7 @@ function createRpgServerTransport(serverModule, options) {
10196
10584
  }
10197
10585
  //#endregion
10198
10586
  //#region src/server-plugin.ts
10199
- async function importWebSocketServer() {
10587
+ async function importWebSocketServer$1() {
10200
10588
  if (typeof process === "undefined" || !process.versions?.node) {
10201
10589
  console.warn("Not in Node.js environment, WebSocket server not available");
10202
10590
  return null;
@@ -10217,7 +10605,7 @@ function serverPlugin(serverModule) {
10217
10605
  name: "server-plugin",
10218
10606
  async configureServer(server) {
10219
10607
  try {
10220
- const WebSocketServerClass = await importWebSocketServer();
10608
+ const WebSocketServerClass = await importWebSocketServer$1();
10221
10609
  if (WebSocketServerClass) {
10222
10610
  wsServer = new WebSocketServerClass({ noServer: true });
10223
10611
  console.log("WebSocket server initialized successfully");
@@ -10418,6 +10806,797 @@ function rpgjs({ server, entryPoints }) {
10418
10806
  ];
10419
10807
  }
10420
10808
  //#endregion
10421
- 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 };
10422
11601
 
10423
11602
  //# sourceMappingURL=index.js.map