Let's get chat formatting **RIGHT**

I’ve looked through the sources of the popular up to date chat formatting plugins.

nobody is doing chat formatting right, and the way it is currently, chat formatting plugins will absolutely step on each others feet, even if they do something minor like add a prefix or replace a word with a clickable text.

So I’m asking, does anyone here have some simple guidelines we can follow, so that we can not ruin each others days?

My suggestions would be to (and I welcome people refuting them)

  1. Use TextTemplates where ever possible. TextTemplates allow other plugins to potentially merge / manipulate your template + arguments and reformat them.
    Additionally it provides users with a standard way to input formatting into configs.

  2. When possible, insert or append text. Don’t just remove the formatting of someone else, or use raw.

  3. If you are providing variables to chat, give them to all the simple text appliers you can find, at event priority LATE this will allow plugins to have had time to apply their templates, and let you populate it with custom variables.

  4. use a plugin prefix for variable names(PvPTagger.player), the scope is shared by all plugins, don’t be a dick by calling yours “player”

  5. Hopefully if we follow these rules, it means we need less “primary formatters” that will format the entire message directly. If you do need to create a “primary formatter” consider using a MessageChannel transformer, if you do, wrap the previous MessageChannel / transformer.

So in short.

Add variables to others templates, if they exist.
insert templateAppliers but these may be overridden…
If you are a primary formatter(whisper, local chat corrupter, chat channels, admin channels), consider creating a custom MessageChannel.

Do these guidelines sound sane? Is it possible for the API to change to force people to work correctly rather then clobber the formatting? Does the API need to change to become simpler?

I’m keen to hear your thoughts.

2 Likes

nobody is doing chat formatting right, and the way it is currently, chat formatting plugins will absolutely step on each others feet, even if they do something minor like add a prefix or replace a word with a clickable text.

Well, I would save clickable text in my plugin, if there was an easy way in API to merge two Text with same plain string, but different formatting into one Text with combined formatting.

EDIT: Oh, wait, I’m wrong. In my plugin, it’s not same plain string, BBCode tags are removed. So, I dunno.

My one chat modification plugin was before there was any documentation on TextTemplates whatsoever. I would probably be able to properly implement this stuff if there was documentation on the things in .text.transform as well.

same string?

For example:

This is my quick example.
+
This is my quick example.

This is my quick example.


Another, more useful, suggestion I have, is add a new command parameter: “Message”. It’s processed like chat message (but without header/footer, obviously), and returns Text. Plugins who implement /msg, /me, /mail, etc. can then be easily compatible with chat color plugins, or censor plugins, etc.

1 Like

I believe plugins that wish to send messages to users can already format the messages so by sending the appropriate event to the event bus? I don’t think just adding a CommandArgument is enough for the processing to happen.

AH! no wait, you could totally do that. just have the CommandArgument throw the event for us. easy.

A lot of these plugins were written for early versions of the API when chat handling was different and documentation was scares. Things will evolve over time.

For the record your suggestion on git was noted and implemented.

2 Likes

Yeah I’m not blaming people for using legacy code, just trying to get things moving in a better direction.

@ryantheleach I’m on board for betterizing text. Several months ago you and I discussed this, but it now looks like we have a way of putting this stuff into practice. I personally would like standardized services/libraries written to handle each type of text replacing/formatting. (Like how economy is handled) There could be one for mustache, BBCode, Markdown etc… I would be interested in assisting with said efforts.

lol agreed. :joy:

I’m not sure I agree that that is something that belongs in the API, however there is a new(ish) level of abstraction of separating messages into a header/body/footer that makes formatting with messages less breaking as you can insert/remove/add elements to each section.

1 Like

With all due respect windy, splitting into header, body, and footer can only go so far. Especially considering that it can’t handle join leave messages gracefully.

If there were some way to grab and style the players name (“player”), the chat message (“message”) (if available), and the “player name container” ("<player>") e.g. "<player> message" this would provide better support to plugins attempting to rename players, changing hovertext, prepending ranks etc.

Main usecases for chat formatting off the top of my head.

"header/playercontainer variables" : Showing ranks / channels / variables / health / factions / nicknames.
"message/body parsing and editing" : player message coloring / styling. adding hoverable “items” adding command references “just type [/help]”

"setting messageChannels on players : ConversationAPI-like redirection of chat. (using this for actual chat channels to me at least, seems rather limiting. I’m on the fence over whether it’s a good idea to ever set the MessageChannel on a player apart from ConversationAPI-like redirection.)

"MessageChannel.transform" : corrupting/garbling messages based on distance to receiver. final replacement for reciever aware formatting.

I’m currently working on a few dummy plugins in order to see how all of this fits together. one I’m attempting to do is “faction relation coloring” that is, the header or prefix, is styled depending on whether the sender and receiver are enemies or allies. However, by the time that transform is called, the textTemplates have been applied already.

Also as Text can’t be extended, there is no way except, make a Text and hope no other plugin corrupts it before it gets transformed. I kind of wish that TextTemplate arguments were Text’s themselves, that would either be replaced before sending to the client by plugins, or at the very very last stage, be replaced by a dummy TextLiteral so the client had something to understand.

That way, a Text containing an argument could be replaced in the transform step.

Any ideas on how to achieve this with the current API?

So, looking over this I think most of this makes sense, however there is one thing that I wonder if I just misunderstood or what. So what I’m a bit unsure about is using a plugin prefix for argument names. Sure, if I had a prefix that I added to chat, it would probably me smart to call that chitchat.prefix, but let’s say that the suggestion about somehow exposing the text of messages like /me to other plugins. At this point I don’t quite feel like it makes sense to use the plugin id in argument names as other plugins would then have to know about what I use for my messages.

While I think it’s a good idea to add the plugin id t argument names if you don’t want that argument to be touched, it also goes a bit against the idea of allowing other plugins to populate these arguments later.

What might work here is that there is some reserved words that have specific meaning in the TextTemplates. For example the argument player always carries the player name. It might carry additional formatting too, but it does NOT carry anything that is not the player name, like prefix, formatting and so on. Same for for example message.

Also, another thing to think about. What is a good way to handle it when two plugins both claim the main TextTemplate? For example, ChitChat exposes the join/leave templates in it’s config and sets those templates as the ones to use. What if there is another plugin that also wants to expose the join/leave messages through it’s TextTemplates?

1 Like

I was imagining that they would be provided by the user, in TextTemplates loaded from configs. It’s not another plugin populating your plugins arguments.

This is how I imagine it happening under the current system.

e.g.

Early or Default event order:

ChatFormatterPro plugin provides a user provided template for chat messages, from the config it’s loaded "<{ChatVariables.health} {ChatFormatterPro.playerName}> : {ChatFormatterPro.message}" which it sets as the message template. (or maybe it’s smarter, and splits it into something that prepends it to the header, it’s up to the plugin)

After Default

ChatFormatterPro provides chat “variables” such as playerName and custom formatting of the message, for “distance decay” letters start going missing the further out you go.

ChatVariables plugin provides chat “variables” such as health, team color, team name, and more.
(in the current system) it visits each element of the formatter, adding to TextTemplateApplier’s it finds, via setParameter(“ChatVariables.health”, getPlayerHealthOrEmpty(player)).

At the moment you would clash, and there is no real solution to fixing that, apart from inserting your text template instead of replacing the message all-together.

Having the concept of a header, body, and footer was a step in the right direction but one thing that peeves me about the current TextTemplates / TextTemplateApplier / MessageFormatter is it feels over-engineered and just makes it harder to fix the problem, edging people towards just editing the current message instead.

Unless there is some glaring issue I’d like to see TextTemplate extend text, so that placeholders can be set directly into the message, and survive until MessageChannel.transform where (reciever,sender) specific formatting can be applied.

One problem I’m running into, whilst creating toy plugins to prototype with, is if you are inserting into the text (or even trying to modify templates of the formatter) how can user’s specify in configs where they would like the variables to appear? Additionally, what about wrapping texts for hover/clicking? + merging what might already be existing hovers…

These problems wouldn’t necessarily be fixed by having placeholder arguments in the text itself, but at least they would be named so it’s possible to find the element you are looking for.

I mean, I hope I’m wrong, and one good example from the developers who wrote the MessageFormatter system to show us how to unravel it all and invalidate my opinion.


Edit: After reading the code and comments on previous TextTemplate / MessageEvent PR’s I have come to the opinion that it was by design that TextTemplate does not implement text, and whilst I disagree it’s unlikely to change as Text currently represents the underlying system 1:1.

Also reading previous comments led me to the “solution” of using TextTemplates anyway for Faction like messaging, by applying and building a TextTemplate on MessageChannel.transform. This has the limitation that previous “placeholders” are lost, and that the correct position to insert must be worked out then. (prepending is easy, anything deeper not so much) Additionally there is no way for ChatFormatting plugins to move this text, exclude it from tickets or private messages.

The closest workaround I can come up with, is having an explicit LiteralText(“FactionsPlaceholder”) and searching, splitting, and replacing that. But it carries similar problems as that of @VcSaJen 's example of merging texts.

Thinking about @VcSaJen’s example, the only way that merging of texts, hover text, and commands is going to work, is if there is some kind of merge strategy or rules used. combine or replace formatting. append hover text, which hover text wins etc.

The biggest problem with merging styles, is that the shape itself of the text is going to change. for messages this isn’t so bad if the message can be identified correctly (like using the body formatter) but for manipulating the header…


Edit2 The main reason why I believe there is resistance to making it easy to format and deal with TextTemplates in MessageChannel.transform is performance. For the majority of use cases when there isn’t per receiver formatting, why should texts be processed over and over. It’s a good point, but I’d like to see a solution where both are possible.

Thinking about @VcSaJen’s
example, the only way that merging of texts, hover text, and commands
is going to work, is if there is some kind of merge strategy or rules
used. combine or replace formatting. append hover text, which hover text
wins etc.

I just think, if, for example, function is Text MergeText(Text text1, Text text2), then text2 take priority over text1 if formatting can’t be combined (for example, Text with red color and Text with green color can’t have combined color, so second Text color “wins” and resulting text have green color).

So, stuff here has mostly died down. Personally I’m mostly done rewriting stuff to better follow these guidelines. There are a few more thoughts that I have though.

One of the thing that @ryantheleach mentioned was to apply the existing MessageChannel#transformMessage on top of yours if you are providing a new one. Problem at the moment (unless i missed something somewhere) is you can’t just merge two messageChannels to get both of their transformers as that will also merge all of their members. An easy way to solve this is to maybe include a few other messageChannels like one that only combines the transformers for example.

You could also have a MessageChannel that behaves the opposite of CombinedMessageChannel. This message channel would only send a message to players that are in both message channels. This would also (from my point of view at least) make it so that plugins that provide custom channels won’t step on each others toes as much. Say for example that you have some sort of minigame plugin on the server that places everyone inside of a minigame in a new MessageChannel. In addition to that you also had a chat plugin that had different channels on the server. With this setup, there wouldn’t be any message that went outside of the minigame channel, while still allowing the chat plugin to operate as it did.

The second thing I think we need to think about is a bit documentation. I’m not talking about plugin documentation or developer documentation. What I’ve more referring to here is documentation of things like TextTemplate and Text that players will see again and again in their configs. I don’t know how far we should go with this, but I feel like we need some central place where players can understand how Text and TextTemplate works, not just from a development standpoint, but also from an user standpoint.

Ok, so that’s mostly it what I had in mind. For now I think the most important part is that plugins will just follow these guidelines. When we are at that point, maybe then we can examine how stuff is looking again.

1 Like

+1. There should be a section on the docs entirely devoted to how everything serializes to and deserializes from the config. Not just Text and TextTemplate, but ItemStack, Location, Entity, etc. Everything that can serialize (read: everything implementing DataSerializable) should either have its own subsection, or be able to be reasonably inferred from subsections of almost identical classes (e.g. ItemStack and ItemStackSnapshot).

I created a pull request for a new MessageChannel type, IntersectedMessageChannel. It behaves in exactly the same way, except that members are required to be in all channels to be included in the getMembers() call. See here for the PR. Of course, it still depends on developers using this to work to fix chat formatting, but hopefully it will be a step in the right direction.