10 Commits

Author SHA1 Message Date
800e7de263 Release v1.1.0 2024-07-07 21:14:20 +02:00
Matheus Clemente
93d1587f61 Update for V12 (#22) 2024-07-07 20:56:41 +02:00
c80346917e Release v1.0.13 2023-06-03 16:17:10 +02:00
c65506ba0c Verified compatibility with Foundry 11 2023-06-03 16:16:21 +02:00
bf59633a16 Release v1.0.12 2022-09-30 21:31:23 +02:00
c75eef77b8 Remove deprecated data attributes 2022-09-30 21:29:39 +02:00
2a7f9cc52e Release v1.0.11 2022-09-16 22:39:46 +02:00
bfc7fb4955 Migrate to Foundryvtt v10 2022-09-16 22:33:54 +02:00
3bdb4d8d6c Release v1.0.10 2021-12-29 23:05:19 +01:00
97c143926a Verified compatibility with Foundry 9 2021-12-29 22:54:02 +01:00
4 changed files with 47 additions and 101 deletions

View File

@@ -1,3 +1,27 @@
## 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

View File

@@ -1,27 +1,28 @@
{
"name": "socketlib",
"id": "socketlib",
"title": "socketlib",
"description": "A library for easier handling of foundry sockets",
"version": "1.0.9",
"minimumCoreVersion" : "0.7.9",
"compatibleCoreVersion" : "0.8.9",
"version": "1.1.0",
"compatibility": {
"minimum": "11",
"verified": "12"
},
"library": true,
"authors": [
{
"name": "Manuel Vögele",
"email": "develop@manuel-voegele.de",
"discord": "Stäbchenfisch#5107"
"discord": "Stäbchenfisch#5107",
"flags": {}
}
],
"esmodules": [
"src/libwrapper_shim.js",
"src/socketlib.js"
],
"url": "https://github.com/manuelVo/foundryvtt-socketlib",
"download": "https://github.com/manuelVo/foundryvtt-socketlib/archive/v1.0.9.zip",
"download": "https://github.com/manuelVo/foundryvtt-socketlib/archive/v1.1.0.zip",
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-socketlib/master/module.json",
"readme": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/README.md",
"changelog": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/CHANGELOG.md",
"bugs": "https://github.com/manuelVo/foundryvtt-socketlib/issues",
"allowBugReporter": false
"bugs": "https://github.com/manuelVo/foundryvtt-socketlib/issues"
}

View File

@@ -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);
}
}
});

View File

@@ -1,4 +1,3 @@
import {libWrapper} from "./libwrapper_shim.js";
import * as errors from "./errors.js";
const RECIPIENT_TYPES = {
@@ -18,9 +17,10 @@ const MESSAGE_TYPES = {
Hooks.once("init", () => {
window.socketlib = new Socketlib();
libWrapper.register("socketlib", "Users.prototype.constructor._handleUserActivity", handleUserActivity);
Hooks.callAll("socketlib.ready");
}, "WRAPPER");
});
Hooks.on("userConnected", handleUserActivity);
class Socketlib {
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.`);
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).`);
return undefined;
}
@@ -55,7 +55,7 @@ class Socketlib {
const existingSocket = this.system;
if (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).`);
}
const newSocket = new SocketlibSocket(systemId, "system");
@@ -90,7 +90,7 @@ class SocketlibSocket {
return this._executeLocal(func, ...args);
}
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.`);
}
return this._sendRequest(name, args, RECIPIENT_TYPES.ONE_GM);
@@ -162,7 +162,7 @@ class SocketlibSocket {
_sendRequest(handlerName, args, recipient) {
const message = {handlerName, args, recipient};
message.id = randomID();
message.id = foundry.utils.randomID();
message.type = MESSAGE_TYPES.REQUEST;
const promise = new Promise((resolve, reject) => this.pendingRequests.set(message.id, {handlerName, resolve, reject, recipient}));
game.socket.emit(this.socketName, message);
@@ -224,7 +224,7 @@ class SocketlibSocket {
else {
switch (recipient) {
case RECIPIENT_TYPES.ONE_GM:
if (!isResponsibleGM())
if (!game.users.activeGM?.isSelf)
return;
break;
case RECIPIENT_TYPES.ALL_GMS:
@@ -303,42 +303,25 @@ class SocketlibSocket {
}
}
function isResponsibleGM() {
if (!game.user.isGM)
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) {
function handleUserActivity(user, active) {
if (!active) {
const modules = Array.from(socketlib.modules.values());
if (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
for (const socket of modules) {
const failedRequests = Array.from(socket.pendingRequests.entries()).filter(([id, request]) => {
const recipient = request.recipient;
const handlerName = request.handlerName;
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.`));
return true;
}
}
else if (recipient instanceof Array) {
if (recipient.includes(userId)) {
request.reject(new errors.SocketlibInvalidUserError(`User '${game.users.get(userId).name}' (${userId}) disconnected while handler '${handlerName}' was being dispatched.`));
if (recipient.includes(user.id)) {
request.reject(new errors.SocketlibInvalidUserError(`User '${user.name}' (${user.id}) disconnected while handler '${handlerName}' was being dispatched.`));
return true;
}
}
@@ -349,5 +332,4 @@ function handleUserActivity(wrapper, userId, activityData={}) {
}
}
}
return result;
}