猫史档案馆


RemoteWrapper.js 1.1.0 - 虚拟端口更新

用户:NomenNomen查看:0 回复:0 评论:0 创建时间:2023-12-28T09:17:10


在这次更新中,为了防止多个工具(例如RemoteWrapper与SocketWrapper发生监听冲突,所以增加了特定端口这一概念,只有处于特定端口的实例会监听特定端口的消息)

Remote.server.js

class RemoteGroups {
    constructor(target) {
        this.target = Array.isArray(target) ? target : [target];
        this.listeners = [];
    }
    send(message) {
        remoteChannel.sendClientEvent(this.target, message);
    }
    sendOne(entity, message) {
        if (this.target.includes(entity)) {
            remoteChannel.sendClientEvent([entity], message)
        }
    }
    each(c) {
        this.target.forEach(v => c(v, this));
    }
    add(entity) {
        this.target.push(entity);
    }
    cancel(entity) {
        this.target = this.target.filter(v => v !== entity);
    }
    stop() {
        this.listeners.forEach(v => v.cancel());
        this.target = [];
        this.listeners = [];
    }
    _register(f, hdls, port) {
        this.listeners.push(remoteChannel.onServerEvent((a) => {
            if (a.args?.port !== port) return;
            if (this.target.includes(a.entity) && typeof a.args?.id !== "number") hdls[a.args?.id]?.(a);
            if (this.target.includes(a.entity)) f(a);
        }));
    }
}

/**
 * @callback WhenGetMessage
 * @param {JSONValue} arg 客户端发送的数据
 */
/**
 * @typedef {Object} RemoteServerEventToken
 * @property {Function} cancel 取消事件
 */
/**
 * @typedef {Object} RemoteServerEvent
 * @property {GameEntity} type 玩家实例
 * @property {WhenGetMessage} handler 回调函数,当事件触发时执行
 * @property {number} id 会话id
 */
class RemoteServerWrapper {
    static quitCode = {
        "1": "传入的实体/实体组不正确",
        "2": "非服务端"
    }
    /**@type {Map<GameEntity, RemoteServerEvent>} */
    static events = new Map();
    /**
     * 监听特定玩家触发的事件
     * @param {GameEntity} entity 要监听的玩家
     * @param {"data"|"communicate"} type 会话类型
     * @param {WhenGetMessage} f 回调函数,当事件触发时执行
     * @param {number?} 会话id,如果不指定则为通用触发器,指定后仅该id的会话会被捕获
     * @returns {RemoteServerEventToken} 调用返回值的cancel属性取消事件
     */
    static listen(entity, type = "data", f, id) {
        if (!this.events.get(entity)) this.events.set(entity, []);
        this.events.get(entity).push({ type, handler: f, id });
        return {
            cancel: () => {
                this.off(entity, f);
            }
        }
    }
    /**
     * 仅监听一次特定玩家触发的事件
     * @param {GameEntity} entity 要监听的玩家
     * @param {"data"|"communicate"} type 会话类型
     * @param {WhenGetMessage} f 回调函数,当事件触发时执行
     * @param {number?} id 会话id,如果不指定则为通用触发器,指定后仅该id的会话会被捕获
     * @returns {RemoteServerEventToken} 调用返回值的cancel属性取消事件
     */
    static once(entity, type = "data", f, id) {
        if (!this.events.get(entity)) this.events.set(entity, []);
        let handler = function (...args) {
            this.off(entity, f);
            return f(...args);
        };
        this.events.get(entity).push({
            type, handler, id
        });
        return {
            cancel: () => {
                this.off(entity, handler);
            }
        }
    }
    /**
     * 向特定客户端广播消息
     * @param {GameEntity} entity 要监听的玩家
     * @param {JSONValue} message 向客户端发送的可序列化数据
     * @param {"data"|"communicate"} type 会话类型
     * @param {number} id 会话id,可通过这个id追踪会话并且监听
     * @returns {number} 会话id
     */
    static send(entity, message, type = "data", id = Date.now()) {
        remoteChannel.sendClientEvent([entity], this.wrap(type, message, id));
        return id;
    }
    static emit(type, ctype, self, id, ...args) {
        if (!this.events.get(type)) this.events.set(type, []);
        this.events.get(type).forEach(v => {
            if (typeof v.handler === "function" && (!id || !v.id || (id && id === v.id)) && v.type === ctype) {
                if (ctype === "data") v.handler.apply(this, args)
                else if (ctype === "communicate") {
                    let res = v.handler.apply(this, args);
                    if (res.then) res.then(r => self.sendOne(type, r, "data", id))
                    else self.sendOne(type, res, "data", id)
                }
            }
        });
    }
    static off(entity, f) {
        if (this.events.get(entity)) this.events.set(entity, this.events.get(entity).filter(v => v.handler !== f));
    }
    static async timer(f, ms) {
        let done = false, rejected = false, timeout;
        try {
            let result = await Promise.race([(async () => { let res = await f(); clearTimeout(timeout); done = true; return res })(),
            new Promise((_r, reject) => {
                timeout = setTimeout(() => {
                    rejected = done = true;
                    reject("Timeout");
                }, ms);
            })]);
            if (result && done && !rejected) return result;
        } catch (err) {
            if (!done) throw err;
            if (rejected) return new Error("Timeout");
        }
    }
    /**
     * 构建RemoteServer实例
     * @constructor
     * @param {GameEntity} target 目标实体,应该是 GameEntity 类型的实例。
     * @param {Object} [options={}] 配置选项。
     * @param {number} [options.port=6000] 端口号,默认为 6000。
     */
    constructor(target, { port } = { port: 6000 }) {
        if ((Array.isArray(target) && target.every(v => v instanceof GameEntity)) || target instanceof GameEntity) this.client = new RemoteGroups(target)
        else throw this._quit("1");
        try { remoteChannel } catch { throw this._quit("2") };
        this.port = port;
        this._start();
    }
    /**
     * 监听受管理的玩家发送的消息
     * @param {"data"|"communicate"} type 会话类型
     * @param {WhenGetMessage} f 回调函数,当事件触发时执行
     */
    onMessage(type, f) {
        this.client.target.forEach(entity => {
            if (!this.constructor.events.get(entity)) this.constructor.events.set(entity, []);
            this.constructor.events.get(entity).push({ type, handler: f });
        });
    }
    /**
     * 向所有当前实例管理的客户端广播消息
     * @param {JSONValue} message 向客户端发送的可序列化数据
     * @param {"data"|"communicate"} type 会话类型
     * @param {number} id 会话id,可通过这个id追踪会话并且监听
     * @returns {number} 会话id
     */
    send(message, type = "data", id = Date.now()) {
        this.client.send(this.wrap(type, message, id));
        return id;
    }
    /**
     * 向特定客户端发送消息
     * @param {GameEntity} entity 指定发送的玩家
     * @param {JSONValue} message 向客户端发送的可序列化数据
     * @param {"data"|"communicate"} type 会话类型
     * @param {number} id 会话id,可通过这个id追踪会话并且监听
     * @returns {number} 会话id
     */
    sendOne(entity, message, type = "data", id = Date.now()) {
        this.client.sendOne(entity, this.wrap(type, message, id));
        return id;
    }
    /**
     * 添加管理特定实体
     * @param {GameEntity} entity 添加管理的实体
     */
    add(entity) {
        this.client.add(entity);
    }
    /**
     * 取消管理特定实体,并且清除共享事件管理器中的受管理单位事件
     * @param {GameEntity} entity 取消管理的实体
     */
    cancel(entity) {
        this.client.cancel(entity);
        this.constructor.events.delete(entity);
    }
    /**
     * 终止当前实例,并且清除共享事件管理器中的受管理单位事件  
     * 这仅应该在你认为你需要放弃该实例并且优化资源时才能调用,停止后尝试调用实例方法可能导致错误
     */
    stop() {
        this.client.target.forEach(v => this.constructor.events.delete(entity));
        this.client.stop();
    }
    /**
     * 向所有当前实例管理的客户端交换数据  
     * 返回的数组包含着所有结果
     * @param {JSONValue} data 向客户端发送的可序列化数据
     * @param {Function} rec 当收到某个客户端返回的数据时触发回调
     * @returns {Promise<JSONValue|Error>[]} 当所有客户端均响应,返回结果数组(即使出错)
     */
    async communicateEntities(data, rec) {
        let r = [];
        this.client.each((t) => {
            r.push(new Promise(resolve => {
                if (t && !t.destroyed) {
                    this.constructor.once(t, "data", (args) => { rec(args.data); resolve(args.data); }, this.send(data, "communicate"));
                }
            }))
        });
        return Promise.all(r);
    }
    /**
     * 向特定的实例交换数据
     * @param {GameEntity} entity 玩家实例
     * @param {JSONValue} data 向客户端发送的可序列化数据
     * @param {Function} rec 当收到某个客户端返回的数据时触发回调
     * @returns {Promise<JSONValue|Error>} 当客户端均响应,返回结果(即使出错)
     */
    async communicate(entity, data, rec) {
        if (!this.client.target.includes(entity)) return null;
        return new Promise(resolve => {
            if (entity && !entity.destroyed) {
                this.constructor.once(entity, "data", (args) => { rec(args.data); resolve(args.data); }, this.sendOne(entity, data, "communicate"))
            }
        });
    }
    wrap(type, data, id) {
        return {
            type,
            data,
            id
        }
    }
    _toData(data) {
        if (typeof data === "object" && data !== null) return data
        else return { data };
    }
    _start() {
        this.client._register(({ entity, args }) => {
            this.constructor.emit(entity, this._toData(args).type, this, this._toData(args).id, args, entity);
        }, {}, this.port);
    }
    _quit(c) {
        return new Error(`[Remote Server] ${this.constructor.quitCode[c]}`);
    }
}

Remote.client.js

function RemoteClientWrapper() {
    let events = [], seed = 2024, dport = 6000,random = () => (seed = (seed * 9301 + 49297) % 233280, parseInt(seed / 233280.0 * 1000000));
    function wrap(type, data, id, port = dport) {
        return { type, data, id, port }
    }
    function send(data, id, type = "data") {
        console.log("sent")
        remoteChannel.sendServerEvent(wrap(type, data, id));
        return id;
    }
    function onMessage(type = "data", f) {
        events.push({ type, handler: f });
        return { cancel() { events = events.filter(v => v.handler !== f); } }
    }
    function communicate(data, f) {
        events.push({
            type: "communicate", handler: (arg) => {
                f(arg);
                events = events.filter(v => v.handler !== f);
            }, id: send(data, random(), "communicate")
        });
    }
    remoteChannel.events.on("client", arg => {
        if (arg.type === "communicate") {
            events.filter(v => v.type === arg.type).forEach(f => {
                let res = f.handler(arg.data);
                if (res.then) res.then(r => send(r, arg.id))
                else send(res, arg.id);
            });
        } else if (arg.type === "data") {
            events.filter(v => v.type === arg.type).forEach(f => f.handler(arg.data));
        }
    });
    return {
        send,
        onMessage,
        communicate
    }
}


回复

上一页1 页 / 共 0下一页