All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
6.0 KiB

1 year ago
  1. /*
  2. * This file is part of the warpgate module (https://github.com/trioderegion/warpgate)
  3. * Copyright (c) 2021 Matthew Haentschke.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, version 3.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. import { logger } from './logger.js'
  18. import { MODULE } from './module.js'
  19. import { Gateway } from './gateway.js'
  20. import { Events } from './events.js'
  21. import { RemoteMutator } from './remote-mutator.js'
  22. import {queueUpdate} from './update-queue.js'
  23. const ops = {
  24. DISMISS_SPAWN : "dismiss", //tokenId, sceneId, userId
  25. EVENT : "event", //name, ...payload
  26. REQUEST_MUTATE: "req-mutate", // ...payload
  27. REQUEST_REVERT: "req-revert", // ...payload
  28. NOTICE: "req-notice",
  29. }
  30. export class Comms {
  31. static register() {
  32. Comms.hooks();
  33. }
  34. static hooks() {
  35. Hooks.on("ready", Comms._ready);
  36. }
  37. static _ready() {
  38. logger.info("Registering sockets");
  39. game.socket.on(`module.${MODULE.data.name}`, Comms._receiveSocket);
  40. }
  41. static _receiveSocket(socketData) {
  42. logger.debug("Received socket data => ", socketData);
  43. /* all users should immediately respond to notices */
  44. if (socketData.op == ops.NOTICE) {
  45. MODULE.handleNotice(socketData.payload.location, socketData.payload.sceneId, socketData.payload.options);
  46. return socketData;
  47. }
  48. queueUpdate( async () => {
  49. logger.debug("Routing operation: ",socketData.op);
  50. switch (socketData.op){
  51. case ops.DISMISS_SPAWN:
  52. /* let the first GM handle all dismissals */
  53. if (MODULE.isFirstGM()) await Gateway.dismissSpawn(socketData.payload.tokenId, socketData.payload.sceneId, socketData.payload.userId);
  54. break;
  55. case ops.EVENT:
  56. /* all users should respond to events */
  57. await Events.run(socketData.eventName, socketData.payload);
  58. break;
  59. case ops.REQUEST_MUTATE:
  60. /* First owner of this target token/actor should respond */
  61. await RemoteMutator.handleMutationRequest(socketData.payload);
  62. break;
  63. case ops.REQUEST_REVERT:
  64. /* First owner of this target token/actor should respond */
  65. await RemoteMutator.handleRevertRequest(socketData.payload);
  66. break;
  67. default:
  68. logger.error("Unrecognized socket request", socketData);
  69. break;
  70. }
  71. });
  72. return socketData;
  73. }
  74. static _emit(socketData) {
  75. game.socket.emit(`module.${MODULE.data.name}`, socketData);
  76. /* always send events to self as well */
  77. return Comms._receiveSocket(socketData);
  78. }
  79. static requestDismissSpawn(tokenId, sceneId) {
  80. /** craft the socket data */
  81. const data = {
  82. op : ops.DISMISS_SPAWN,
  83. payload : { tokenId, sceneId, userId: game.user.id }
  84. }
  85. return Comms._emit(data);
  86. }
  87. /*
  88. * payload = {userId, tokenId, sceneId, updates, options}
  89. */
  90. static requestMutate(tokenId, sceneId, { updates = {}, options = {} } = {}, onBehalf = game.user.id ) {
  91. /* insert common fields */
  92. const payload = {
  93. userId: onBehalf,
  94. tokenId,
  95. sceneId,
  96. updates,
  97. options,
  98. }
  99. /* craft the socket data */
  100. const data = {
  101. op: ops.REQUEST_MUTATE,
  102. payload
  103. }
  104. return Comms._emit(data);
  105. }
  106. static requestRevert(tokenId, sceneId, {mutationId = undefined, onBehalf = game.user.id, options = {}}) {
  107. /* insert common fields */
  108. const payload = {
  109. userId: onBehalf,
  110. tokenId,
  111. sceneId,
  112. mutationId,
  113. options,
  114. }
  115. /* craft the socket data */
  116. const data = {
  117. op: ops.REQUEST_REVERT,
  118. payload
  119. }
  120. return Comms._emit(data);
  121. }
  122. static requestNotice(location, sceneId = canvas.scene?.id, options = {}) {
  123. const data = {
  124. op: ops.NOTICE,
  125. payload: {
  126. sceneId,
  127. location,
  128. options,
  129. }
  130. }
  131. return Comms._emit(data);
  132. }
  133. static packToken(tokenDoc) {
  134. const tokenData = tokenDoc.toObject();
  135. delete tokenData.actorData;
  136. let actorData = tokenDoc.actor?.toObject() ?? {};
  137. actorData.token = tokenData;
  138. return actorData;
  139. }
  140. /**
  141. * Allow custom events to be fired using the Warp Gate event system. Is broadcast to all users, including the initiator.
  142. * Like Hooks, these functions cannot be awaited for a response, but all event functions executing on a given client
  143. * will be evaluated in order of initial registration and the processing of the event functions will respect
  144. * (and await) returned Promises.
  145. *
  146. * @param {string} name Name of this event. Watches and triggers use this name to register themselves.
  147. * Like Hooks, any string can be used and it is dependent upon the watch or trigger registration to monitor the correct event name.
  148. * @param {object} [payload={sceneId: canvas.scene.id, userId: game.user.id}] eventData {Object} The data that will be
  149. * provided to watches and triggers and their condition functions.
  150. * @param {string} [onBehalf=game.user.id] User ID that will be used in place of the current user in the
  151. * cases of a relayed request to the GM (e.g. dismissal).
  152. *
  153. * @returns {Object} Data object containing the event's payload (execution details), and identifying metadata about
  154. * this event, sent to all watching and triggering clients.
  155. */
  156. static notifyEvent(name, payload = {}, onBehalf = game.user.id) {
  157. /** insert common fields */
  158. payload.sceneId = canvas.scene.id;
  159. payload.userId = onBehalf;
  160. /* craft the socket data */
  161. const data = {
  162. op : ops.EVENT,
  163. eventName: name,
  164. payload
  165. }
  166. return Comms._emit(data);
  167. }
  168. }