pmmp / PocketMine-MP

A server software for Minecraft: Bedrock Edition in PHP
https://pmmp.io
GNU Lesser General Public License v3.0
3.28k stars 1.56k forks source link

Transition away from Minecraft formatting codes for internal usage #2988

Open dktapps opened 5 years ago

dktapps commented 5 years ago

Description

This issue proposes that we should change the standard for text formatting within PocketMine-MP to use a different non-crap system like HTML. This includes CommandSender->sendMessage(), logging, the title bar, and so on.

As appropriate, HTML formatting would be translated to Minecraft formatting on the fly at the network layer.

Justification

Minecraft's standard formatting codes suck. There are only 16 colours, the formatting set is limited, and there's no way to enclose text.

The primary problem that motivated this issue is the fact that it's not possible to revert to the previous formatting using Minecraft colour codes. In HTML you can do things like this:

<blue>This text is blue, but <red>these words are red</red>, and these words are blue again.</blue>

There's no way to "close" or encapsulate a block of text with formatting codes with Minecraft formatting. For example:

&l&aBold green text, &0black text&r and "default" text.

The &r formatting code isn't fit for purpose because it removes all formatting. In addition, the enclosement problem is particularly a headache with translations when translation parameters contain their own formatting codes.

In addition, IDEs like PhpStorm natively support HTML formatting codes and show a nice colour marker in the left column of the editor.

Alternative methods

It's possible we could alter the existing system and extend it to add more formatting capabilities, but this would also necessitate on-the-fly network translation, and it's not worth the effort.

Consider the following:

&4red text&r

vs

TextFormat::RED . "red text" . TextFormat::RESET

vs

<red>red text</red>
ghost commented 5 years ago

I'm pretty sure <red></red> isn't valid HTML. https://developer.mozilla.org/en-US/docs/Web/HTML/Element

SOF3 commented 5 years ago

It doesn't need to be HTML compliant HTML. Let's call it XML instead.

SOF3 commented 5 years ago

Note the potential problem of unclosed tags in individual return strings. It might be appropriate to box each string in containers, e.g.

function _(string $xml) : FormattedString{
  validate_xml_or_throw($xml);
  return new FormattedString($xml);
}
SOF3 commented 5 years ago

Due to the infamous verbosity of XML, it might be worth considering, at the cost of lower IDE support, to use an alternative syntax for markup, such as {red(some red text)}, etc.

lukeeey commented 5 years ago

Hmmm

{yellow(Hello {red(%s)}!) How are you doing {light_purple(today)}?}

Kinda reminds me of callback hell.

I suppose its better than this

<yellow>Hello <red>%s</red>!
How are you doing <light_purple>today</light_purple>? </yellow>
UnknownOre commented 5 years ago

The html tags really looks good

dktapps commented 5 years ago

The bottom line is that we'd need some custom pre-processed system. Better to have an entirely new system than to confuse people with magic extra Minecraft codes that only exist in PM.

Frago9876543210 commented 5 years ago

implementation on pure PHP

<?php

declare(strict_types=1);

use pocketmine\utils\Terminal;

require_once "vendor/autoload.php";

define("ALLOWED_TAGS", [
    "black" => "0",
    "dark_blue" => "1",
    "dark_green" => "2",
    "dark_aqua" => "3",
    "dark_red" => "4",
    "dark_purple" => "5",
    "gold" => "6",
    "gray" => "7",
    "dark_gray" => "8",
    "blue" => "9",
    "green" => "a",
    "aqua" => "b",
    "red" => "c",
    "light_purple" => "d",
    "yellow" => "e",
    "white" => "f",

    "obfuscated" => "k",
    "bold" => "l",
    "italic" => "o"
]);

function parse(string $subject, ?string $color = null) : string{
    if(($count = preg_match_all("/(?:[^<]+)(?![^<]*>|[^<>]*<\/)|<(\S+)>(.*?)<\/\\1>/", $subject, $matches)) === 0){
        throw new InvalidArgumentException("HTML parse error");
    }
    $result = "";
    for($i = 0; $i < $count; ++$i){
        if(empty($tag = $matches[1][$i])){
            $result .= $color . $matches[0][$i] . "\xc2\xa7r";
        }elseif(isset(ALLOWED_TAGS[$tag])){
            try{
                $result .= parse($matches[2][$i], "\xc2\xa7" . ALLOWED_TAGS[$tag]);
            }catch(InvalidArgumentException $e){
                //NOOP
            }
        }else{
            throw new InvalidArgumentException("Unexpected HTML tag \"$tag\"");
        }
    }
    return $result;
}

$subject = "<blue>This text is blue, but <red>these words are red</red>, and these words are blue again.</blue>";
$output = parse($subject);

echo $subject . PHP_EOL;
echo $output . PHP_EOL;

Terminal::init();
Terminal::writeLine($output);
SOF3 commented 4 years ago

Adding this to 4.0 milestone as this involves potential backward compatibility difficulties such as #3270

Gianluxx commented 4 years ago

Just do it for internals, Thanks. Don't break every plugin that uses chat colors (which one doesn't?) when a lot of developers no longer support their now working plugins.

dktapps commented 4 years ago

Can we try reading the issue description instead of just assuming some nonsense rationale? Thanks

SOF3 commented 4 years ago

I am not exactly sure if hardcoding color values like red, aqua, etc., is a good idea for forward compatibility.

It is possible to add these codes for mnemonic reasons in the end-user-facing code, but internally, it might be a better idea to store colors in HSB format, and when converting to ANSI or Minecraft color codes, compute the most proximal color code available.