Compare commits

..

4 Commits

Author SHA1 Message Date
farling42 a28ca6cc6d Add new maintainer + add github action file 2025-03-20 15:55:13 +00:00
Farling 5c995983f0 Update links to new location 2025-03-20 14:32:54 +00:00
Manuel Vögele 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
5 changed files with 173 additions and 101 deletions
+139
View File
@@ -0,0 +1,139 @@
# GitHub Actions workflow for creating a new FoundryVTT module release.
#
# Useful References:
# - https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
# - https://docs.github.com/en/actions/learn-github-actions/contexts
# - https://docs.github.com/en/actions/learn-github-actions/environment-variables
#
# Troubleshooting Checklist:
# - Is the module's manifest file valid JSON?
# You can test your manifest file using https://jsonlint.com/.
#
# - Does the module's manifest have all the required keys?
# See https://foundryvtt.com/article/module-development/#manifest for more
# information.
#
# - Are all the proper files and directories being included in the release's
# module archive ("module.zip")?
# Check that the correct files are being passed to the `zip` command run
# in the "Create Module Archive" step below.
#
# - Is the release tag the proper format?
# See the comments for the "Extract Version From Tag" step below.
#
# - Is a GitHub release being published?
# This workflow will only run when a release is published, not when a
# release is updated. Furthermore, note that while a GitHub release will
# (by default) create a repository tag, a repository tag will not create
# or publish a GitHub release.
#
# - Has the module's entry on FoundryVTT's module administration site
# (https://foundryvtt.com/admin) been updated?
#
name: Create Module Files For GitHub Release
env:
# The URL used for the module's "Project URL" link on FoundryVTT's website.
project_url: "https://github.com/${{github.repository}}"
# A URL that will always point to the latest manifest.
# FoundryVTT uses this URL to check whether the current module version that
# is installed is the latest version. This URL should NOT change,
# otherwise FoundryVTT won't be able to perform this check.
latest_manifest_url: "https://github.com/${{github.repository}}/releases/latest/download/module.json"
# The URL to the module archive associated with the module release being
# processed by this workflow.
release_module_url: "https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/module.zip"
on:
# Only run this workflow when a release is published.
# To modify this workflow when other events occur, see:
# - https://docs.github.com/en/actions/using-workflows/triggering-a-workflow
# - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
# - https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
#
# Note that some steps may depend on context variables that are only
# available for release events, so if you add other events, you may need to
# alter other parts of this workflow.
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
# Extract version embedded in the tag.
# This step expects the tag to be one of the following formats:
# - "v<major>.<minor>.<patch>" (e.g., "v1.2.3")
# - "<major>.<minor>.<patch>" (e.g., "1.2.3")
#
# The version will be used by later steps to fill in the value for the
# "version" key required for a valid module manifest.
- name: Extract Version From Tag
id: get_version
uses: battila7/get-version-action@v2
# Modify "module.json" with values specific to the release.
# Since the values for the "version" and "url" keys aren't known ahead of
# time, the manifest file in the repository is updated with these values.
#
# While this does modify the manifest file in-place, the changes are not
# commited to the repository, and only exist in the action's filesystem.
- name: Modify Module Manifest With Release-Specific Values
id: sub_manifest_link_version
uses: cschleiden/replace-tokens@v1
with:
files: 'module.json'
env:
VERSION: ${{steps.get_version.outputs.version-without-v}}
URL: ${{ env.project_url }}
MANIFEST: ${{ env.latest_manifest_url }}
DOWNLOAD: ${{ env.release_module_url }}
# Create a "module.zip" archive containing all the module's required files.
# If you have other directories or files that will need to be added to
# your packaged module, add them here.
- name: Create Module Archive
run: |
# Note that `zip` will only emit warnings when a file or directory
# doesn't exist, it will not fail.
zip \
`# Options` \
--recurse-paths \
`# The name of the output file` \
./module.zip \
`# The files that will be included.` \
module.json \
CHANGELOG.md \
README.md \
src/
# Don't forget to add a backslash at the end of the line for any
# additional files or directories!
# Update the GitHub release with the manifest and module archive files.
- name: Update Release With Files
id: create_version_release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
name: ${{ github.event.release.name }}
draft: ${{ github.event.release.unpublished }}
prerelease: ${{ github.event.release.prerelease }}
token: ${{ secrets.GITHUB_TOKEN }}
artifacts: './module.json, ./module.zip'
tag: ${{ github.event.release.tag_name }}
body: ${{ github.event.release.body }}
+5
View File
@@ -1,3 +1,8 @@
## 1.1.0
### Compatibility
- Updated for compatibilty with Foundry 12 (thanks Clemente!)
## 1.0.13 ## 1.0.13
### Compatibility ### Compatibility
- Verified compatibility with Foundry 11 - Verified compatibility with Foundry 11
+18 -11
View File
@@ -2,14 +2,22 @@
"id": "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.13", "version": "1.1.0",
"compatibility": { "compatibility": {
"minimum": "10", "minimum": "11",
"verified": "11" "verified": "12"
}, },
"library": true, "library": true,
"authors": [ "authors": [
{ {
"name": "Farling",
"url": "https://github.com/farling42",
"email": "foundryvtt@amusingtime.uk",
"discord": "farling",
"ko-fi": "farling",
"patreon": "amusingtime"
},
{
"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",
@@ -17,13 +25,12 @@
} }
], ],
"esmodules": [ "esmodules": [
"src/libwrapper_shim.js",
"src/socketlib.js" "src/socketlib.js"
], ],
"url": "https://github.com/manuelVo/foundryvtt-socketlib", "url": "https://github.com/farling42/foundryvtt-socketlib",
"download": "https://github.com/manuelVo/foundryvtt-socketlib/archive/v1.0.13.zip", "download": "https://github.com/farling42/foundryvtt-socketlib/archive/v1.1.0.zip",
"manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-socketlib/master/module.json", "manifest": "https://raw.githubusercontent.com/farling42/foundryvtt-socketlib/master/module.json",
"readme": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/README.md", "readme": "https://github.com/farling42/foundryvtt-socketlib/blob/master/README.md",
"changelog": "https://github.com/manuelVo/foundryvtt-socketlib/blob/master/CHANGELOG.md", "changelog": "https://github.com/farling42/foundryvtt-socketlib/blob/master/CHANGELOG.md",
"bugs": "https://github.com/manuelVo/foundryvtt-socketlib/issues" "bugs": "https://github.com/farling42/foundryvtt-socketlib/issues"
} }
-61
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);
}
}
});
+11 -29
View File
@@ -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() {
@@ -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.id < game.user.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;
} }