/* * This file is part of the warpgate module (https://github.com/trioderegion/warpgate) * Copyright (c) 2021 Matthew Haentschke. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import { logger } from './logger.js' import { MODULE } from './module.js' import { Gateway } from './gateway.js' import { Events } from './events.js' import { RemoteMutator } from './remote-mutator.js' import {queueUpdate} from './update-queue.js' const ops = { DISMISS_SPAWN : "dismiss", //tokenId, sceneId, userId EVENT : "event", //name, ...payload REQUEST_MUTATE: "req-mutate", // ...payload REQUEST_REVERT: "req-revert", // ...payload NOTICE: "req-notice", } export class Comms { static register() { Comms.hooks(); } static hooks() { Hooks.on("ready", Comms._ready); } static _ready() { logger.info("Registering sockets"); game.socket.on(`module.${MODULE.data.name}`, Comms._receiveSocket); } static _receiveSocket(socketData) { logger.debug("Received socket data => ", socketData); /* all users should immediately respond to notices */ if (socketData.op == ops.NOTICE) { MODULE.handleNotice(socketData.payload.location, socketData.payload.sceneId, socketData.payload.options); return socketData; } queueUpdate( async () => { logger.debug("Routing operation: ",socketData.op); switch (socketData.op){ case ops.DISMISS_SPAWN: /* let the first GM handle all dismissals */ if (MODULE.isFirstGM()) await Gateway.dismissSpawn(socketData.payload.tokenId, socketData.payload.sceneId, socketData.payload.userId); break; case ops.EVENT: /* all users should respond to events */ await Events.run(socketData.eventName, socketData.payload); break; case ops.REQUEST_MUTATE: /* First owner of this target token/actor should respond */ await RemoteMutator.handleMutationRequest(socketData.payload); break; case ops.REQUEST_REVERT: /* First owner of this target token/actor should respond */ await RemoteMutator.handleRevertRequest(socketData.payload); break; default: logger.error("Unrecognized socket request", socketData); break; } }); return socketData; } static _emit(socketData) { game.socket.emit(`module.${MODULE.data.name}`, socketData); /* always send events to self as well */ return Comms._receiveSocket(socketData); } static requestDismissSpawn(tokenId, sceneId) { /** craft the socket data */ const data = { op : ops.DISMISS_SPAWN, payload : { tokenId, sceneId, userId: game.user.id } } return Comms._emit(data); } /* * payload = {userId, tokenId, sceneId, updates, options} */ static requestMutate(tokenId, sceneId, { updates = {}, options = {} } = {}, onBehalf = game.user.id ) { /* insert common fields */ const payload = { userId: onBehalf, tokenId, sceneId, updates, options, } /* craft the socket data */ const data = { op: ops.REQUEST_MUTATE, payload } return Comms._emit(data); } static requestRevert(tokenId, sceneId, {mutationId = undefined, onBehalf = game.user.id, options = {}}) { /* insert common fields */ const payload = { userId: onBehalf, tokenId, sceneId, mutationId, options, } /* craft the socket data */ const data = { op: ops.REQUEST_REVERT, payload } return Comms._emit(data); } static requestNotice(location, sceneId = canvas.scene?.id, options = {}) { const data = { op: ops.NOTICE, payload: { sceneId, location, options, } } return Comms._emit(data); } static packToken(tokenDoc) { const tokenData = tokenDoc.toObject(); delete tokenData.actorData; let actorData = tokenDoc.actor?.toObject() ?? {}; actorData.token = tokenData; return actorData; } /** * Allow custom events to be fired using the Warp Gate event system. Is broadcast to all users, including the initiator. * Like Hooks, these functions cannot be awaited for a response, but all event functions executing on a given client * will be evaluated in order of initial registration and the processing of the event functions will respect * (and await) returned Promises. * * @param {string} name Name of this event. Watches and triggers use this name to register themselves. * Like Hooks, any string can be used and it is dependent upon the watch or trigger registration to monitor the correct event name. * @param {object} [payload={sceneId: canvas.scene.id, userId: game.user.id}] eventData {Object} The data that will be * provided to watches and triggers and their condition functions. * @param {string} [onBehalf=game.user.id] User ID that will be used in place of the current user in the * cases of a relayed request to the GM (e.g. dismissal). * * @returns {Object} Data object containing the event's payload (execution details), and identifying metadata about * this event, sent to all watching and triggering clients. */ static notifyEvent(name, payload = {}, onBehalf = game.user.id) { /** insert common fields */ payload.sceneId = canvas.scene.id; payload.userId = onBehalf; /* craft the socket data */ const data = { op : ops.EVENT, eventName: name, payload } return Comms._emit(data); } }