RanvierMUD / core

Core engine code for Ranvier
https://ranviermud.com
MIT License
45 stars 41 forks source link

Broadcast proxy #126

Open NuSkooler opened 3 years ago

NuSkooler commented 3 years ago

(This is a early PR for discussion / feedback)

2nd half of PR here: https://github.com/RanvierMUD/core/pull/126

This brings in the concept of a BroadcastProxy. If you notice it is heavily based on the discussion and work in #59. The major difference is the ability to completely delegate or "proxy" messages to a handler that can both a) apply it's own coloring/style (or strip existing) and b) control how the data is output.

The ranvier.json setup looks something like this:

"broadcastProxies" : {
  "telnet" : {
    "require" : "/path/to/MyProxy"
  }
}

Where a basic implementation of a proxy may look like the following:

const ansi = require('sty');
ansi.enable(); // force ansi on even when there isn't a tty for the server

const { Broadcast } = require('ranvier');

module.exports = class SkullCrashTelnetBroadcastProxy {

    proxy(target, message, options) {
        if (message.trim().length === 0) {
            return target.socket.write(message);
        }

        let targetMessage = options.formatter(target, message);
        if (target.broadcastHandler) {
            targetMessage = ansi.parse(targetMessage);
            target.broadcastHandler(target, targetMessage, options);
        } else {
            let targetMessage = options.formatter(target, message);
            targetMessage = options.wrapWidth ? Broadcast.wrap(targetMessage, options.wrapWidth) : ansi.parse(targetMessage);
            target.socket.write(targetMessage);
        }
    }

    configure(config) {
        this.config = config;
    }
};

If you notice above a target Player may be assigned a broadcastHandler (or whatever property you like) method. In the real world I'm using this for turn based battle screens where chat and combat log information fall into a specific region of the layout while in this mode.

Streams need to attach if they wish to play:

// first, provide a "identifier" getter
get identifier() {
  return 'telnet';
}

// ... and attach
state.BroadcastProxyRegistry.attach(stream);

For now I left the existing Broadcast.XXXX methods mostly alone, but I agree with some of the statements in #59 in that using a options{} is easier to deal with.

There is also the issue of some of the formatter's being hard coded to colorify which really just plays with sty. I'm getting around this right now by using my own channels.js implementation that skips the passed in formatter and instead just uses my own scheme.

Feedback and thoughts welcome!