Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 800e7de263 | |||
|
|
93d1587f61 | ||
| c80346917e | |||
| c65506ba0c | |||
| bf59633a16 | |||
| c75eef77b8 | |||
| 2a7f9cc52e | |||
| bfc7fb4955 | |||
| 3bdb4d8d6c | |||
| 97c143926a | |||
| 4afb56f9be | |||
| 7315c0fa4c | |||
| d8125e7592 | |||
| 48121fe6c6 | |||
| 51c02f81ea | |||
| a1179579f9 |
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,3 +1,42 @@
|
|||||||
|
## 1.1.0
|
||||||
|
### Compatibility
|
||||||
|
- Updated for compatibilty with Foundry 12 (thanks Clemente!)
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0.13
|
||||||
|
### Compatibility
|
||||||
|
- Verified compatibility with Foundry 11
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0.12
|
||||||
|
### Compatibility
|
||||||
|
- Foundry 10 will no longer show a deprecation warning when modules or systems register in socketlib
|
||||||
|
|
||||||
|
## 1.0.11
|
||||||
|
### Compatibility
|
||||||
|
- Updated for compatibility with Foundry 10
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0.10
|
||||||
|
### Compatibility
|
||||||
|
- Verified compatibility with Foundry 9
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0.9
|
||||||
|
### Compatibility
|
||||||
|
- Verified compatibility with Foundry 0.8.9
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0.8
|
||||||
|
### Compatibility
|
||||||
|
- Verified compatibility with Foundry 0.8.8
|
||||||
|
|
||||||
|
|
||||||
|
## 1.0.7
|
||||||
|
### Compatibility
|
||||||
|
- Verified compatibility with Foundry 0.8.7
|
||||||
|
|
||||||
|
|
||||||
## 1.0.6
|
## 1.0.6
|
||||||
### Compatibility
|
### Compatibility
|
||||||
- Verified compatibility with Foundry 0.8.5
|
- Verified compatibility with Foundry 0.8.5
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Manuel Vögele
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
19
module.json
19
module.json
@@ -1,27 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "socketlib",
|
"id": "socketlib",
|
||||||
"title": "socketlib",
|
"title": "socketlib",
|
||||||
"description": "A library for easier handling of foundry sockets",
|
"description": "A library for easier handling of foundry sockets",
|
||||||
"version": "1.0.6",
|
"version": "1.1.0",
|
||||||
"minimumCoreVersion" : "0.7.9",
|
"compatibility": {
|
||||||
"compatibleCoreVersion" : "0.8.5",
|
"minimum": "11",
|
||||||
|
"verified": "12"
|
||||||
|
},
|
||||||
"library": true,
|
"library": true,
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Manuel Vögele",
|
"name": "Manuel Vögele",
|
||||||
"email": "develop@manuel-voegele.de",
|
"email": "develop@manuel-voegele.de",
|
||||||
"discord": "Stäbchenfisch#5107"
|
"discord": "Stäbchenfisch#5107",
|
||||||
|
"flags": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"esmodules": [
|
"esmodules": [
|
||||||
"src/libwrapper_shim.js",
|
|
||||||
"src/socketlib.js"
|
"src/socketlib.js"
|
||||||
],
|
],
|
||||||
"url": "https://github.com/manuelVo/foundryvtt-socketlib",
|
"url": "https://github.com/manuelVo/foundryvtt-socketlib",
|
||||||
"download": "https://github.com/manuelVo/foundryvtt-socketlib/archive/v1.0.6.zip",
|
"download": "https://github.com/manuelVo/foundryvtt-socketlib/archive/v1.1.0.zip",
|
||||||
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-socketlib/master/module.json",
|
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-socketlib/master/module.json",
|
||||||
"readme": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/README.md",
|
"readme": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/README.md",
|
||||||
"changelog": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/CHANGELOG.md",
|
"changelog": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/CHANGELOG.md",
|
||||||
"bugs": "https://github.com/manuelVo/foundryvtt-socketlib/issues",
|
"bugs": "https://github.com/manuelVo/foundryvtt-socketlib/issues"
|
||||||
"allowBugReporter": false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
// Copyright © 2021 fvtt-lib-wrapper Rui Pinheiro
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// A shim for the libWrapper library
|
|
||||||
export let libWrapper = undefined;
|
|
||||||
|
|
||||||
Hooks.once('init', () => {
|
|
||||||
// Check if the real module is already loaded - if so, use it
|
|
||||||
if(globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) {
|
|
||||||
libWrapper = globalThis.libWrapper;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback implementation
|
|
||||||
libWrapper = class {
|
|
||||||
static get is_fallback() { return true };
|
|
||||||
|
|
||||||
static register(module, target, fn, type="MIXED", {chain=undefined}={}) {
|
|
||||||
const is_setter = target.endsWith('#set');
|
|
||||||
target = !is_setter ? target : target.slice(0, -4);
|
|
||||||
const split = target.split('.');
|
|
||||||
const fn_name = split.pop();
|
|
||||||
const root_nm = split.splice(0,1)[0];
|
|
||||||
const _eval = eval; // The browser doesn't expose all global variables (e.g. 'Game') inside globalThis, but it does to an eval. We copy it to a variable to have it run in global scope.
|
|
||||||
const obj = split.reduce((x,y)=>x[y], globalThis[root_nm] ?? _eval(root_nm));
|
|
||||||
|
|
||||||
let iObj = obj;
|
|
||||||
let descriptor = null;
|
|
||||||
while(iObj) {
|
|
||||||
descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name);
|
|
||||||
if(descriptor) break;
|
|
||||||
iObj = Object.getPrototypeOf(iObj);
|
|
||||||
}
|
|
||||||
if(!descriptor || descriptor?.configurable === false) throw `libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.`;
|
|
||||||
|
|
||||||
let original = null;
|
|
||||||
const wrapper = (chain ?? type != 'OVERRIDE') ? function() { return fn.call(this, original.bind(this), ...arguments); } : function() { return fn.apply(this, arguments); };
|
|
||||||
|
|
||||||
if(!is_setter) {
|
|
||||||
if(descriptor.value) {
|
|
||||||
original = descriptor.value;
|
|
||||||
descriptor.value = wrapper;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
original = descriptor.get;
|
|
||||||
descriptor.get = wrapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(!descriptor.set) throw `libWrapper Shim: '${target}' does not have a setter`;
|
|
||||||
original = descriptor.set;
|
|
||||||
descriptor.set = wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.configurable = true;
|
|
||||||
Object.defineProperty(obj, fn_name, descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import {libWrapper} from "./libwrapper_shim.js";
|
|
||||||
import * as errors from "./errors.js";
|
import * as errors from "./errors.js";
|
||||||
|
|
||||||
const RECIPIENT_TYPES = {
|
const RECIPIENT_TYPES = {
|
||||||
@@ -18,9 +17,10 @@ const MESSAGE_TYPES = {
|
|||||||
|
|
||||||
Hooks.once("init", () => {
|
Hooks.once("init", () => {
|
||||||
window.socketlib = new Socketlib();
|
window.socketlib = new Socketlib();
|
||||||
libWrapper.register("socketlib", "Users.prototype.constructor._handleUserActivity", handleUserActivity);
|
|
||||||
Hooks.callAll("socketlib.ready");
|
Hooks.callAll("socketlib.ready");
|
||||||
}, "WRAPPER");
|
});
|
||||||
|
|
||||||
|
Hooks.on("userConnected", handleUserActivity);
|
||||||
|
|
||||||
class Socketlib {
|
class Socketlib {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -38,7 +38,7 @@ class Socketlib {
|
|||||||
console.error(`socketlib | Someone tried to register module '${moduleName}', but no module with that name is active. As a result the registration request has been ignored.`);
|
console.error(`socketlib | Someone tried to register module '${moduleName}', but no module with that name is active. As a result the registration request has been ignored.`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (!module.data.socket) {
|
if (!module.socket) {
|
||||||
console.error(`socketlib | Failed to register socket for module '${moduleName}'. Please set '"socket":true' in your manifset and restart foundry (you need to reload your world - simply reloading your browser won't do).`);
|
console.error(`socketlib | Failed to register socket for module '${moduleName}'. Please set '"socket":true' in your manifset and restart foundry (you need to reload your world - simply reloading your browser won't do).`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ class Socketlib {
|
|||||||
const existingSocket = this.system;
|
const existingSocket = this.system;
|
||||||
if (existingSocket)
|
if (existingSocket)
|
||||||
return existingSocket;
|
return existingSocket;
|
||||||
if (!game.system.data.socket) {
|
if (!game.system.socket) {
|
||||||
console.error(`socketlib | Failed to register socket for system '${systemId}'. Please set '"socket":true' in your manifest and restart foundry (you need to reload your world - simply reloading your browser won't do).`);
|
console.error(`socketlib | Failed to register socket for system '${systemId}'. Please set '"socket":true' in your manifest and restart foundry (you need to reload your world - simply reloading your browser won't do).`);
|
||||||
}
|
}
|
||||||
const newSocket = new SocketlibSocket(systemId, "system");
|
const newSocket = new SocketlibSocket(systemId, "system");
|
||||||
@@ -90,7 +90,7 @@ class SocketlibSocket {
|
|||||||
return this._executeLocal(func, ...args);
|
return this._executeLocal(func, ...args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!game.users.find(isActiveGM)) {
|
if (!game.users.activeGM) {
|
||||||
throw new errors.SocketlibNoGMConnectedError(`Could not execute handler '${name}' (${func.name}) as GM, because no GM is connected.`);
|
throw new errors.SocketlibNoGMConnectedError(`Could not execute handler '${name}' (${func.name}) as GM, because no GM is connected.`);
|
||||||
}
|
}
|
||||||
return this._sendRequest(name, args, RECIPIENT_TYPES.ONE_GM);
|
return this._sendRequest(name, args, RECIPIENT_TYPES.ONE_GM);
|
||||||
@@ -162,7 +162,7 @@ class SocketlibSocket {
|
|||||||
|
|
||||||
_sendRequest(handlerName, args, recipient) {
|
_sendRequest(handlerName, args, recipient) {
|
||||||
const message = {handlerName, args, recipient};
|
const message = {handlerName, args, recipient};
|
||||||
message.id = randomID();
|
message.id = foundry.utils.randomID();
|
||||||
message.type = MESSAGE_TYPES.REQUEST;
|
message.type = MESSAGE_TYPES.REQUEST;
|
||||||
const promise = new Promise((resolve, reject) => this.pendingRequests.set(message.id, {handlerName, resolve, reject, recipient}));
|
const promise = new Promise((resolve, reject) => this.pendingRequests.set(message.id, {handlerName, resolve, reject, recipient}));
|
||||||
game.socket.emit(this.socketName, message);
|
game.socket.emit(this.socketName, message);
|
||||||
@@ -224,7 +224,7 @@ class SocketlibSocket {
|
|||||||
else {
|
else {
|
||||||
switch (recipient) {
|
switch (recipient) {
|
||||||
case RECIPIENT_TYPES.ONE_GM:
|
case RECIPIENT_TYPES.ONE_GM:
|
||||||
if (!isResponsibleGM())
|
if (!game.users.activeGM?.isSelf)
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
case RECIPIENT_TYPES.ALL_GMS:
|
case RECIPIENT_TYPES.ALL_GMS:
|
||||||
@@ -303,42 +303,25 @@ class SocketlibSocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isResponsibleGM() {
|
function handleUserActivity(user, active) {
|
||||||
if (!game.user.isGM)
|
if (!active) {
|
||||||
return false;
|
|
||||||
const connectedGMs = game.users.filter(isActiveGM);
|
|
||||||
return !connectedGMs.some(other => other.data._id < game.user.data._id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isActiveGM(user) {
|
|
||||||
return user.active && user.isGM;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUserActivity(wrapper, userId, activityData={}) {
|
|
||||||
const user = game.users.get(userId);
|
|
||||||
const wasActive = user.active;
|
|
||||||
const result = wrapper(userId, activityData);
|
|
||||||
|
|
||||||
// If user disconnected
|
|
||||||
if (!user.active && wasActive) {
|
|
||||||
const modules = Array.from(socketlib.modules.values());
|
const modules = Array.from(socketlib.modules.values());
|
||||||
if (socketlib.system)
|
if (socketlib.system)
|
||||||
modules.concat(socketlib.system);
|
modules.concat(socketlib.system);
|
||||||
const GMConnected = Boolean(game.users.find(isActiveGM));
|
|
||||||
// Reject all promises that are still waiting for a response from this player
|
// Reject all promises that are still waiting for a response from this player
|
||||||
for (const socket of modules) {
|
for (const socket of modules) {
|
||||||
const failedRequests = Array.from(socket.pendingRequests.entries()).filter(([id, request]) => {
|
const failedRequests = Array.from(socket.pendingRequests.entries()).filter(([id, request]) => {
|
||||||
const recipient = request.recipient;
|
const recipient = request.recipient;
|
||||||
const handlerName = request.handlerName;
|
const handlerName = request.handlerName;
|
||||||
if (recipient === RECIPIENT_TYPES.ONE_GM) {
|
if (recipient === RECIPIENT_TYPES.ONE_GM) {
|
||||||
if (!GMConnected) {
|
if (!game.users.activeGM) {
|
||||||
request.reject(new errors.SocketlibNoGMConnectedError(`Could not execute handler '${handlerName}' as GM, because all GMs disconnected while the execution was being dispatched.`));
|
request.reject(new errors.SocketlibNoGMConnectedError(`Could not execute handler '${handlerName}' as GM, because all GMs disconnected while the execution was being dispatched.`));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (recipient instanceof Array) {
|
else if (recipient instanceof Array) {
|
||||||
if (recipient.includes(userId)) {
|
if (recipient.includes(user.id)) {
|
||||||
request.reject(new errors.SocketlibInvalidUserError(`User '${game.users.get(userId).name}' (${userId}) disconnected while handler '${handlerName}' was being dispatched.`));
|
request.reject(new errors.SocketlibInvalidUserError(`User '${user.name}' (${user.id}) disconnected while handler '${handlerName}' was being dispatched.`));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,5 +332,4 @@ function handleUserActivity(wrapper, userId, activityData={}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user