#adventure-help

1 messages · Page 3 of 1

cloud vapor
#

this and workarounds are explained in the linked docs above

prisma mason
#

I set the display name

#

so I don't see how it would be a problem?

sand drift
#

Because we don't deconstruct and mangle items sent over the network for that

cloud vapor
#

It wouldn't be a problem as is explained in the docs

prisma mason
#

I see

#

okay, seems like I will run into too many problems trying to implement localization.

#

thanks anyways ❤️

cloud vapor
#

Not really, it's a really simple system

#

The only problem you'll have is translating the text

#

adventure handles everything else

prisma mason
#

eh there's another layer

#

I implemented my own gradient and rainbow algoritm

sand drift
#

I mean, yea, you're not just tossing raw strings around but need to formulate a source of strings coming from elsewhere

prisma mason
#

so unless I translate server side it isn't possible

#

I might go out on a limb here, but AFAIK most of my players are from the USA or UK.

cloud vapor
#

and what im saying is that server side translations are incredibly easy

#

once you have the lang files

prisma mason
#

I suppose

#

I guess I could have some sort of public repo for lang files for my items

#

so rn I have baked Components in an enum, I'd replace them with a key and some sort of style?

#

MiniMessage is also a good option definetly

cloud vapor
#

You can just replace them with a translatable component yeah - give the above linked docs a read they are very thourough

prisma mason
#

I'm still confused about the thing cat said, something about items not allowing it?

Some implementations may not use the GlobalTranslator in every area, or at all. For example, Paper does not use it for items

#

so if I use a TranslatableComponent when setting the display name of an ItemMeta it would be fine or illegal?

sweet hornet
#

If you use a custom server-wide resource pack and provides the translations there, is will works yes. But server-side translations won't work on items, no

cloud vapor
#

You'd need to manually call GlobalTranslator#render(Component, Locale) for item names

sweet hornet
#

Some typos found while reading the doc https://docs.advntr.dev/localization.html:

-    // An an example, we will hard-code a check for a specific key here.
+    // As an example, we will hard-code a check for a specific key here.
  • Line 24 of that same snippet:
-    if (key.equals("mytranslation.colorful") && locale == Locale.US) {
+    if (component.key().equals("mytranslation.colorful") && locale == Locale.US) {

It provides a simpler way OF creating a Translator
But as english is not my native language, I'm not 100% sure about this

cloud vapor
#

Feel free to submit a PR

hoary plume
#

Is <!shadow> a self-closing tag?
Because I noticed that if I try to use <!shadow>text</shadow> in-between font tags, that it doesn't seem to parse the </shadow> tag, causing weird text displays.

cloud vapor
#

</!shadow>

tired sage
cloud vapor
#

It's close, but I doubt it's 1:1

#

Why?

tired sage
#

the chatbox size on the web doesn't match the one in-game

#

I'll demonstrate, sec

#

Input: ----------------------------------------------
In-Game:

#

web-ui

#

Tho I think that it used to match last time I used it

rare sage
#

i mean, chat box width is configurable so that isn't something you should be relying upon anyway

cloud vapor
#

Indeedy

#

PRs open if you wish to tweak the box to be more accurate to vanilla, but it's not something that we will spend time on bc of the reason Emily mentioned

tired sage
tired sage
prisma mason
#

hey so, I've been thinking of contributing to adventure, I feel like the rainbow tag requires a saturation option. Mostly becuase having saturation to 1 isn't pleasant to look at unlike lower saturations. Examples from my own implementation are below. This was a voting process from my players and option 3 won by a long shot since it is closer to #FFF than saturation 1.

#

I'm not sure if this is the right channel but README said discod

cloud vapor
#

can't think of one that would make sense off the top of my head

prisma mason
#

Well I was thinking before the reverse parameter !

#

But it conflicts with phase

warped marsh
#

Hi, I should send a message to the player that could contain URLs but I noticed that by doing player.sendMessage(Component.text(myText)), the URLs are no longer "recognized".
Is there any simple way to solve it? Or do I necessarily have to do something like split the string at each space, check for each element if it is a URL and eventually make it a clickable URL?

Thanks

cloud vapor
#

You need to attach a click event to your text - see Component#clickEvent(ClickEvent) and ClickEvent.openUrl

warped marsh
sand drift
#

afaik you can use replaceText and a regex to do it

#

Can't advise on that now though

sterile rampart
#

there's an example in the legacy serializer impl

sweet hornet
#

Isn’t there an option for automatically converting URLs to clickable components using the PlainTextSerializer?

#

Oh no you're right that's the LegacyComponentSerializer.Builder#extractUrls() method, but in fact there's no particular reason to not have something similar in the PlainTextComponentSerializer? I mean, URLs are not legacy, right? 😄

sterile rampart
#

it shouldnt be in any serializer, it's only in legacy to match craftbukkit behaviour which is to match how vanilla used to behave before components existed

#

mostly cuz there's no One Perfect Link Regex

sweet hornet
#

Placeholder.unparsed()

sand drift
#

You would create your own MM instance which supports only the things you want to expose to end users

cloud vapor
# sweet hornet `Placeholder.unparsed()`

This is the correct answer - your user input should be inserted into the minimessage string using an unparsed placeholder (which means the content will not be parsed by minimessage)

sand drift
#

Yes

#

you would just resolve their input using that into a component to do what you want with it

#

if you don't want a user to be able to do anything in terms of tags, then you could just toss it into a component directly

#

parse why?

#

if you want something that's parsed so people can do stuff, just parse it yourself and slap it in as a component

#

that way you don't need to worry about it affecting the rest of the tree as you know exactly where it is in there

sweet hornet
#

Then deserialize out user input on one hand, and then deserialize the rest of your input with the user parsed stuff using Placeholder.component()

sand drift
#

I more questioning why you're wanting to parse the user input instead of just shoving it into a string component

#

do you expect them to be able to use formatting in there, for ex

sweet hornet
#

How the hell did you ended up in that situation? Please explain your XY

sand drift
#

I'm not really 100% on what you're saying

#

The important thing to remember is that components are a tree, and a lot of smarts around dealing with this sorta thing is operating with that knowledge to get the outcome you desire

sand drift
#

Basically, no

sweet hornet
#

Without any control over the placeholder replacement made by the other plugin, you won't be able to safely do anything

sand drift
#

maybe you could wrap them in some tag that you can treat as raw text

#

or something to that effect

#

but, being so far detatched from the data is just a disaster waiting to happen

sweet hornet
sand drift
#

Yea, true

sweet hornet
#

The "best" option I can see right now is using a regexp to extract the two user provided parts from the string, then handle them separately as we suggested above. It won't be exactly accurate if the user tries to break it, it might produces result not matching he's exact inputs, BUT you'll be able 100% sure it won't break your part

#

Let me give you an example

cloud vapor
#

honestly the best thing you can do is not do that

#

dont use your awful plib plugin, edit the existing one to use minimessage

sweet hornet
#

You said the string was formatted as:

<bold><green>Name: {name}</green>; <red>Description: {description}</red></bold>

Using a regexp like this:

<bold><green>Name: (.*)</green>; <red>Description: (.*)</red></bold>

You'll be able to extract the 2 groups into 2 separate variables, and then do:

Pattern pattern = Pattern.compile("<bold><green>Name: (.*)</green>; <red>Description: (.*)</red></bold>");
MiniMessage userMM = MiniMessage.miniMessage(); // Your MiniMessage instance with only safe tags. You should not keep `MiniMessage.miniMessage()`
String resultComputedByOtherPluginFromTemplateAndUserInput = ...;
Matcher matcher = pattern.matcher(resultComputedByOtherPluginFromTemplateAndUserInput);
if (matcher.matches()) {
    String name = matcher.group(1);
    String description = matcher.group(2);
    MiniMessage.miniMessage().deserialize("<bold><green>Name: <name></green>; <red>Description: <description></red></bold>",
            Placeholder.component("name", userMM.deserialize(name)),
            Placeholder.component("description", userMM.deserialize(description)));
} else {
    // This should never happen if you have control over the template
}
cloud vapor
#

christ

cloud vapor
#

better idea yes

#

see MiniMessage#stripTags

sweet hornet
#

The only thing that could "go wrong" is a user providing these values:

  • Name: Foo</green>; <red>Description: Bar
  • Description: Foobar

It will leads to:

<bold><green>Name: Foo</green>; <red>Description: Bar</green>; <red>Description: Foobar</red></bold>

Then the regexp will probably extract:

  • Name: Foo
  • Description: Bar</green>; <red>Description: Foobar

Which is not exactly what the user wrote. But it won't mess with your rendering

cloud vapor
#

check if input equals output and block if it doesnt

gritty tundra
#

seems like ClickEvent.callback won't work on spigot, is there an alternative? I need to run more complex logic because the teleport command doesn't take a world argument (unless I'm overlooking it in the docs). it sounds like my best bet for now is to make a custom command in my plugin and get the clickevent to run that command, which then runs my logic

cloud vapor
#

correct that is your best option

#

the callback click event is just a fancy run-command click event anyway

#

but implementing platforms can handle the command part for you bc they live in the future

gritty tundra
#

I was tempted to drop spigot support because of stuff like that lol

cloud vapor
#

good idea :p

gritty tundra
#

I was going to but decided against it for now, sorry

#

the effort to continue supporting is minimal so if that changes...

radiant tendon
#

I'm making a plugin to provide a placeholder <head:[player name]>. Now I have a TagResolver set up for that, but is there a way to add it to the global MiniMessage instance rather than my own instance using MiniMessage.builder so that other plugins can just use the placeholder text? Or is that just a MiniPlaceholders thing?

sterile rampart
#

the global instance is fixed, that will not change until we have namespaced tags

radiant tendon
#

ooo namespaced tags

#

love that

#

so my best bet for right now is likely using MiniPlaceholders then, since this placeholder must be a component?

sterile rampart
#

ya

radiant tendon
#

dang, well thanks for the help!

gritty tundra
#

Any idea why MiniMessage.miniMessage().deserialize("<#fc2150>test"); would throw this error? I use this same code in other places and the error isn't thrown, so I'm not sure if something is causing the shadow color call in one spot but not others.

Caused by: java.lang.NoSuchMethodError: 'myplugin.libs.adventure.text.format.ShadowColor myplugin.libs.adventure.text.format.StyleGetter.shadowColor()'

wet kestrel
#

Looks like you're mixing and matching versions you're relocating?

gritty tundra
#

let me double check, but I don't think so because I control all versions from one spot.

wet kestrel
#

I mean, if you have adventure code calling a method that doens't exist, what else would it be?

gritty tundra
#

I don't believe I am but I'll look more. the only place in my repo I define a version is in the main gradle file:

deps.adventure-api=net.kyori:adventure-api:4.20.0
deps.adventure-bukkit=net.kyori:adventure-platform-bukkit:4.3.4
deps.adventure-minimessage=net.kyori:adventure-text-minimessage:4.20.0

Only one subproject includes the lib and as I mentioned the same code in other class works fine, with the same text string. I'm not sure where my difference is

gritty tundra
#

I do have a subproject shading the api when it shouldn't be, so I assume thats my prob and will fix it and retry. I'm not sure why it's even being included there, prob some gradle mistake

#

ok it's from a dependency

#

another dep I uses also includes it so when I shade that dep I also get kyori stuff so I have mixed versions

sage river
#

it seems that Minecraft now doesn't store element type for ListTag (which means you can add a tag of any type to the list) and effectively indentifies it when needed (identifyRawElementType method). is it something that adventure-nbt would adopt?

cloud vapor
#

i believe there's an open PR for it

#

could do with reviews/etc

sterile rampart
#

there's an open issue for heterogeneous lists, i've got some local wip

#

the on-disk format hasn't changed at all though, heterogeneous lists are just boxed in compounds

cloud vapor
#

i for one wish we didnt bother with all this anti-woke stuff and just stayed with homogeneous lists 😒

sage river
#

okaay, thanks for the responses

deep scarab
#

ok why did you guys remove like all non-basic methods from Index?

#

like keyOr(), valueOr(), getting the maps, etc

sterile rampart
#

"remove"?

deep scarab
#

i think its giving me an old adventure version for some reason

#

oh never mind i changed the paper dev bundle version incorrectly

#

when updating

#

ill look on gh next time first 💛

deep scarab
#

anyone know a way to parse an md5 BaseComponent to adventure?

#

im updating deprecated APIs so this is rather fun

wet kestrel
#

If it's deprecated, time to remove it! 😄

#

Alternatively, isn't there a bungee serializer? And there's always the ol' classic serialize to json, deserialize from json.

deep scarab
#

yea thats pretty much what i did rn

sterile rampart
#

uhh that's json-to-json-to-json, you should be able to use the gson serializer directly lol

wet kestrel
#

They're just helping confirm all the serializers work, abby! 😆

deep scarab
sterile rampart
#

look at GsonComponentSerializer

deep scarab
#

just did, what's the input?

#

like would this work?

wet kestrel
#

Try it and see!

deep scarab
#

no time

#

jk nah ill leave it in a bad way cause people arent intended to use it anyway)

steel cedar
#

Is it possible to run some code before every translation (with the GlobalTranslator in this case)? Or does one need to make their own translator to do this?
I want to use ICU4J to format numbers using RuleBasedNumberFormat. My idea was to check for a specific key in the given TranslatableComponent, such as ordinal and then use the argument to translate, like 5 -> 5th.
Is this the way to do it or do you guys recommend another approach?

cloud vapor
#

Well translators are just interfaces you can implement and do whatever you want with so yeah you can run arbitrary code

steel cedar
#

And of course I still want to be able to translate stuff using MiniMessage

cloud vapor
#

If it's MiniMessage I'd just rec making a custom tag, e.g. ordinal:5 or smth

steel cedar
steel cedar
cloud vapor
#

can't you just make the placement tag insert the ordinal?

steel cedar
#

I'd prefer to only translate it when it's sent, not upon constructing

cloud vapor
#

Why?

steel cedar
#

It's for a hologram, and I don't want to make a separate hologram for every player

#

Seems unnecessary

cloud vapor
#

Right fair enough, then make a custom tag

#

you can see e.g. TagResolver.WithoutArguments for a simple lambda one

sweet hornet
#

Wait, but in this case, the translation is not done client-side, but server-side, right?

steel cedar
#

Yes

sweet hornet
#

Your hologram is a textdisplay?

steel cedar
#

yes

sweet hornet
#

When are server-side translations resolved when using components in a textdisplay? If it's the same as items, it won't work at all, iirc

steel cedar
#

No, it works fine

#

Just like chat

#

Only itemstacks don't work afaik

sweet hornet
#

Hmm, interesting, so for example we can use server-side translations on entity names? Since its just entity data, just like the textdisplay's content

steel cedar
#

Since it works for text displays, I'd guess so, yes

steel cedar
cloud vapor
#

Are you using a MiniMessageTranslator?

steel cedar
#

Yes

#

well, MiniMessageTranslationStore

cloud vapor
#

Then just use a tag argument

#

and Argument.tag

#

or Argument.tagReslver or w/e

steel cedar
#

is the target supposed to be null when using this code? The handler does get called, but without a target

val title = translatable(
  "npc.podium.hologram.placement",
  color,
  Argument.tagResolver(TagResolver.builder()
    .tag("placement") { queue, context ->
      val locale = context.targetAsType(Player::class.java).locale()
      val formatted = RuleBasedNumberFormat(locale, RuleBasedNumberFormat.ORDINAL).format(placement)

      Tag.inserting(text(formatted))
    }
    .build())
)
cloud vapor
#

You need to set the target using Argument.target

steel cedar
#

right... but I don't know the target at this point

cloud vapor
#

hm right

#

I suppose we could make the target default to Locale if not set, and let the target argument overwrite it

steel cedar
#

that'd be nice

cloud vapor
#

let me whip up a pr

cloud vapor
steel cedar
#

thank you

oak hamlet
#

hi, I don't know what I am doing wrong. I would like to add support to items name and lore with MiniMessages and legacy colors, this is my code:

    public ItemBuilder withName(String name, String[] placeholders, String[] replaces) {
        ItemMeta meta = item.getItemMeta();
        if (meta == null || name == null) return this;

        String formatted = applyPlaceholder(name, placeholders, replaces);
        Component component = Legacy.RESET_ITALIC.append(SupremeTags.getMiniMessage().deserialize(formatted));

        meta.displayName(component);
        item.setItemMeta(meta);
        return this;
    }
public class Legacy {

    public static final LegacyComponentSerializer AMPERSAND_SERIALIZER = LegacyComponentSerializer.builder()
            .character('&')
            .hexColors()
            .useUnusualXRepeatedCharacterHexFormat()
            .build();

    public final static Component RESET_ITALIC = Component.text()
            .decoration(TextDecoration.ITALIC, false)
            .build();

    public static Component title(String text) {
        return RESET_ITALIC.append(AMPERSAND_SERIALIZER.deserialize(text));
    }

    public static Component component(String text) {
        return AMPERSAND_SERIALIZER.deserialize(text);
    }

    public static List<Component> component(List<String> texts) {
        return texts.stream()
                .map(Legacy::component)
                .collect(Collectors.toList());
    }

}
public class LegacyColorPostProcessor implements UnaryOperator<Component> {

    private static final TextReplacementConfig LEGACY_REPLACEMENT_CONFIG = TextReplacementConfig.builder()
            .match(Pattern.compile(".*"))
            .replacement((matchResult, build) -> Legacy.component(matchResult.group()))
            .build();

    @Override
    public Component apply(Component component) {
        return component.replaceText(LEGACY_REPLACEMENT_CONFIG);
    }

}
#

public class LegacyColorPreProcessor implements UnaryOperator<String> {

    @Override
    public String apply(String component) {
        return component.replace("§", "&");
    }

}
#
MiniMessage miniMessage = MiniMessage.builder()
            .preProcessor(new LegacyColorPreProcessor())
            .postProcessor(new LegacyColorPostProcessor())
            .build();
#

the problem is that this code cannot parse MiniMessages color format, like <red> color:#5555ff etc.

#

what am I doing wrong?

cloud vapor
#

You're massively overcomplicating things here

#

Are you trying to support both legacy and MiniMessage?

oak hamlet
#

yes!

cloud vapor
#

Unfortunately we don't provide support for mixing formats like that

oak hamlet
#

since when? I remember in older version this was working?

wet kestrel
#

Mixing them has never been a good idea. You should one-time migrate outdated configs from legacy, and then going forward use minimessage.

cloud vapor
#

I'm talking about using both formats (legacy and minimessage) together, we don't and have never provided support for that

oak hamlet
#

so I need to change configs from legacy color codes to all mini messages like: <color:#5555ff>, right?

cloud vapor
#

Yep

#

fyi you can shorthand that as <#5555ff>

oak hamlet
# cloud vapor Yep
16:55:16 WARN]: Caused by: net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl: Legacy formatting codes have been detected in a MiniMessage string - this is unsupported behaviour. Please refer to the Adventure documentation (https://docs.advntr.dev) for more information.
[16:55:16 WARN]:        <§x§f§f§1§4§9§3>Deep Pink
sand drift
#

I mean, you translated it to a legacy ampersand string

wet kestrel
#

So... you left code in that does nightmare conversions to legacy. 😆

oak hamlet
#

I removed legacy conversions and changed everything to minimessages format

cloud vapor
#

You definitely didn't - can you show us the updated code?

oak hamlet
#
  Component nameComponent = SupremeTags.getMiniMessage().deserialize(name);
        meta.displayName(nameComponent);
#

MiniMessage miniMessage = MiniMessage.builder().build();

cloud vapor
#

and that plain MiniMessage instance is what SupremeTags.getMiniMessage() returns?

wet kestrel
#

Print name prior to going into deserialize. I bet it's already been poked at.

oak hamlet
#
06:19 WARN]: Caused by: net.kyori.adventure.text.minimessage.internal.parser.ParsingExceptionImpl: Legacy formatting codes have been detected in a MiniMessage string - this is unsupported behaviour. Please refer to the Adventure documentation (https://docs.advntr.dev) for more information.
[17:06:19 WARN]:         §eDescription:
mental vineBOT
oak hamlet
#

idk whats going here

oak hamlet
# oak hamlet '

all souts from here are string going to MiniMessages deserialization

rare sage
#

complete code always helps

oak hamlet
#

from config file

rare sage
#

code

#

code

#

code

#

thanks discord

oak hamlet
#
       if (section.contains("Name")) {
                String name = section.getString("Name");
                builder.withName(applyPlaceholder(name, placeholders, replaces));
            }

    private static String applyPlaceholder(String text, String[] placeholders, String[] replacers) {
        if (text == null || text.isEmpty()) return text;

        if (placeholders != null && placeholders.length > 0 && placeholders.length == replacers.length) {
            text = StringUtils.replaceEach(text, placeholders, replacers);
        }

        return text;
    }


    public ItemBuilder withName(String name) {
        ItemMeta meta = item.getItemMeta();
        if (meta == null) return this;

        System.out.println("With name: " + name);
        Component nameComponent = SupremeTags.getMiniMessage().deserialize(name);
        meta.displayName(nameComponent);
        item.setItemMeta(meta);
        return this;
    }
``` this is my friend's code which is pretty bad so please don't judge it, I am only adding this mini messages feature
sweet hornet
oak hamlet
#
    public ItemBuilder withLore(List<String> lore, String[] placeholders, String[] replaces) {
        ItemMeta meta = item.getItemMeta();
        if (meta == null || lore == null || lore.isEmpty()) return this;

        List<Component> toAdd = new ArrayList<>(lore.size());

        for (String str : lore) {
            String formatted = applyPlaceholder(str, placeholders, replaces);
            System.out.println("formatted lore: " + formatted);
            Component loreComponent = SupremeTags.getMiniMessage().deserialize(formatted);

            toAdd.add(loreComponent);
        }

        meta.lore(toAdd);
        item.setItemMeta(meta);
        return this;
    }
cloud vapor
#

applyPlaceholder looks sus

sweet hornet
#

And SupremeTags.getMiniMessage()

oak hamlet
# cloud vapor applyPlaceholder looks sus
  private static String applyPlaceholder(String text, String[] placeholders, String[] replacers) {
        if (text == null || text.isEmpty()) return text;

        if (placeholders != null && placeholders.length > 0 && placeholders.length == replacers.length) {
            text = StringUtils.replaceEach(text, placeholders, replacers);
        }

        return text;
    }
sweet hornet
#

Then StringUtils.replaceEach, if that's part of your codebase and not a standard library like apache commons

cloud vapor
#

atp i'd suggest stepping through your code with a debugger and working out where the legacy is getting added

cloud vapor
#

also would suggest making sure that you are running the code that you are writing - possible you could be running an outdated jar

oak hamlet
rare sage
# oak hamlet '

is the error present in this same log? this suggests it's all working fine

sweet hornet
#

That's true

#

If you get all that log, then you don't have an error

oak hamlet
mental vineBOT
oak hamlet
#
 public ItemBuilder replaceLine(String placeholder, List<String> lines) {
        ItemMeta meta = item.getItemMeta();
        if (meta == null || meta.getLore() == null || placeholder == null || placeholder.isEmpty()) return this;

        List<String> toAdd = new ArrayList<>();

        for (String str : meta.getLore()) {
            System.out.println("replaceLine: " + str);
            if (StringUtils.containsIgnoreCase(str, placeholder)) {
                for (String line : lines) {
                    toAdd.add(str.replace(placeholder, line));
                }
                continue;
            }
            toAdd.add(str);
        }

        return withLore(toAdd);
    }
#

I think this part is the problem

#

it is using meta.getLore()

rare sage
#

yep, that would be it

sweet hornet
#

You must use meta.lore() instead to get the lore as components

oak hamlet
#

yup

tough peak
#

Do I need to write

actionText = actionText.append(Component.text("Hey"));

?

prisma mason
#

Yes

wet kestrel
#

Components are immutable. You could instead use the builder, Component.text()

tough peak
#
Component actionText = Component.empty();
            for (Component l : itemLore.lines()) actionText = actionText.appendNewline().append(l);
wet kestrel
#

Okay. Is there a question embedded in this message or are you just sharing what you're using adventure for? Not sure if I should be answering with something here 🙂

tough peak
wet kestrel
#

Okay! Have fun 🙂

cloud vapor
#

I'd consider looking into Component#join - in your example you could simply do Component.join(JoinConfiguration.newlines(), itemLore.lines())

rare sage
#

the client does not render newlines everywhere

tough peak
midnight spire
#

you can theoretically achieve multilines everywhere with a custom font in a resource pack if you really need them

tough peak
midnight spire
#

yeah

tough peak
#

Also is there a way to style a component like when using MiniMessage.miniMessage().deserialize()?

cloud vapor
#

how do you mean?

tough peak
# cloud vapor how do you mean?

~~```java
Component component = ...;

The component is from an item lore
This component looks like a normal string (no colors and such)
I want to make the colors appear~~
robust wharf
#

you will have to share more code

#

Component.text("<red>hey") obviously will not be colored, you will either have to shove that thru minimessage or do Component.text("hey", RED)

tough peak
robust wharf
#

so you should write {text: "hey", color: "red" } instead

prisma mason
sweet hornet
#

Actionbar i guess

#

Or maybe title, looks similar

tough peak
prisma mason
#

Ic

prisma mason
#

is implementing ForwardingAudience a bad idea?

#

class structure is: Game -> Teams -> Players

sand drift
#

it's intended to be implemented

prisma mason
#

a Game holds many teams

#

A Team holds players

#

so I should do it?

#

I'm just confused why there are so many methods to implement if all it does is forward

#

oh wait nvm

#

I guess I don't have to implement all of those methods? Why did IJ list all of them

wet kestrel
#

It offers to implement them but it only needs the one.

#

And it's a great idea!

prisma mason
#

👍

gritty tundra
#

how come click events etc break every time there's an update? my plugin, which shades kyori for spigot support, doesn't do anything on click when try it on paper for 1.21.5. I know that is experimental but I've noticed each time there's a new mc update click event stuff breaks until we update

robust wharf
#

because mojang changed the format

hoary plume
#

I have a bit of trouble with translatables...
Trying to use the <lang_or> tag of MiniMessage to display a translation or fallback. Sadly is there no documentation about the syntax but I assume it must be <lang_or:<key>:'<fallback>'[:<replacement1>:<replacement2>:...>?

#

Tested it real quick and it seems to be the case.
Tho, I now notice that replacements seem to reset the current formatting within the translation string.

For example, I have §7Position: §b%s§7, §b%s§7, §b%s§7, §b%s in my lang file, but the lore displays the replacements as the purple color:

#

Is there any way in MiniMessage to avoid this from happening?

tawny bolt
#

does it work if you don't mix it with legacy formatting?

hoary plume
#

Haven't tried that

#

But I assume that MM is adding the replacements as Components and that MC resets/forces their formatting over everything else.

#

So I probs can fix it by putting <aqua> before the replacement values.

sweet hornet
hoary plume
#

Well, main issue is that Minimessage obviously doesn't work in translation files.

#

So at least there would I need them

cloud vapor
hoary plume
#

I mean without having to hijack the entire translation thing

cloud vapor
#

"hijack"?

tawny bolt
#

huh? that is built-in

cloud vapor
#

it's part of adventure

tawny bolt
#

unless you mean vanilla translation files in resource packs, those indeed only support § formatting

hoary plume
#

I talk about MC's translate thing

cloud vapor
#

yeah that's how it works

#

read the docs :p

hoary plume
#

Like why would I talk about anything else when I mention translatables

cloud vapor
hoary plume
#

....I do use a resource pack you know...

cloud vapor
#

they aren't very powerful

hoary plume
#

Should I care?

#

I have the translation files there, they work. End of Story for me.

cloud vapor
#

Okay cool, glad we could help then 👍

hoary plume
#

Tho, docs really need to add <lang_or>.

#

Not sure how tho

#

I think as a sub-section of the lang tag itself should be good?

cloud vapor
#

Feel free to submit a PR

#

A subsection sounds good

hoary plume
#

Since when was fallback a feature?
I think around 1.20?

hoary plume
#

It's 1.19.4 apparently

jolly sage
#

When i add a deserialized MiniMessage string to a lore of an item, it becomes italic but I cannot just remove the Italic decoration from the deserialized component because they could be explicitly italic (with <i> tags), what can I do to make it not italic by default?

sweet hornet
#

.decorationIfAbsent(ITALIC, false)

wet kestrel
#

That's covered by the faq

sweet hornet
#

TL;DR: Most methods come with *IfAbsent alternatives, applying the style/color/decoration... only when not explicitly already present

jolly sage
#

thanks

fathom mulch
#

does the adventure api have conversion to and from the nms equivalent classes?

wet kestrel
#

Adventure doesn't, because it is platform agnostic

#

Platforms will certainly implement that somewhere.

fathom mulch
#

oh ok i'll ask in the other channel then, thanks

quaint monolith
#

Does Paper actually convert anywhere? I thought it just added adventure->packet stuff instead and passed adventure all the way through

#

Well I guess it'd have to convert eventually to save

sterile rampart
#

there are conversions ya

#

but full conversions are mostly avoided

gritty tundra
#

4.20.0 click/hover events are broken for me on spigot/paper, is that a known issue that's fixed on snapshots? usually there's a fix by now but I'm starting to think something else is wrong for me

wet kestrel
#

On 1.21.4 or 1.21.5?

gritty tundra
#

1.21.5 sorry

sterile rampart
#

using platform-bukkit?

gritty tundra
#

yes, v4.3.4

#

I support spigot as well as paper so I shade/relocate them

sterile rampart
#

not updated yet, PRs welcome

robust wharf
#

(there is a PR)

sterile rampart
#

not for all of the 1.21.5 issues

wet kestrel
#

Can you demonstrate it being an actual problem? I've only ever seen people complain who don't actually have a problem but just want things faster, so far.

#

What's the unit for that?

#

So an average of 0.0002 nano seconds?

#

And you consider that slow?

sonic grove
#

That's still not slow

wet kestrel
#

I asked for unit and you said nanos

sweet hornet
#

Nevertheless, keep in mind that MiniMessage is rather well optimized, and if you face real performance issues on your server (I mean, you have low TPS and MM stuff shows up in your Spark), there's a chance it's still "your" (of your plugins' developer) fault. By that, I mean that your code would use MM too much, badly, or in a very unoptimized manner (i.e. recomputing texts every tick while they only ever change every second, that would result in a 19 useless calls, you get the idea)

umbral wharf
#

sounds like its not laggy at all

cloud vapor
#

In our server we process dozens of strings every tick for up to hundreds of players and haven't noticed a single performance issue. 0.18 milliseconds is basically nothing and we do lots of benchmarking work, including proper JMH suites, and have never noticed any issues. If you do encounter performance problems, please do share spark reports, specific benchmarks or other profiler data and we'll be happy to look into it!

cloud vapor
#

MiniMessage strings, some small, some giant - some in packets, some in api, etc etc

#

Yep, our most taxing inventory can have hundreds of strings per player

#

Nothing special at all, we just use MiniMessage 🤷

#

That's why I was saying that spark/profiler reports are good, they'll be able to help us work out what specifically the issue is, and what we can improve upon

sand drift
#

A parallel stream probably hurts more than it helps for this

sterile rampart
#

lots of questions that could be answered with profiling lol

cloud vapor
#

We just do everything p much sync in a list lol

#

like i said, nothing special

night niche
#

Is there an easy way to replace text in a component? My immediate first thought was to just serialize to json, do the replace on that string then deserialize back to component to avoid losing any formatting but I feel like there may be amore elegant solution.

cloud vapor
#

What's the xy? can point you towards a good solution then

night niche
#

Obfuscating the playername(s) in death messages if they are invis

#

so it'd be replacing the playername with "<obfuscated>--------"

cloud vapor
#

Well death messages are all translatable components with, I think, the first component always being the name

#

So the easiest solution would be to just remake the component, swapping out that first argument

night niche
#

its the more complicated cases that we are trying to make sure stays the same

#

where the person killing somebody else can also be invis

night niche
cloud vapor
#

Just iterate through the arguments then, plain text each component, check if it's a player name and then obfuscates

wet kestrel
#

Until your player "diamond_sword" wins a fight while invisible 😀

night niche
night niche
cloud vapor
#

it's the arguments you'd want to iterate over, death messages are just one translatable component

night niche
#

oh so arguments are a separate thing from the children of the death message component?

cloud vapor
#

The arguments of the translatable component are inserted into the resulting string in the %s spots

night niche
#

Is a Translateable component a child/implementation of the Component class or something?

#

Since the .deathMessage() method returns a Component and I'm not seeing any .arguments() or similarly named method, but maybe I'm looking in the wrong place

cloud vapor
#

Yes, Component is an interface implemented by all component types, e.g. TextComponent, TranslatableComponent, etc

night niche
#

so am I right in assuming that I just cast that specific Component to TranslateableComponent?

sweet hornet
night niche
#

fair point

night niche
# cloud vapor The arguments of the translatable component are inserted into the resulting stri...
val component = e.deathMessage()!! as TranslatableComponent
val args = component.arguments()
val newArgs = mutableListOf<TranslationArgument>()
args.forEach {
    newArgs.add(TranslationArgument.component(Component.text("THIS IS A TEST")))
}

component.arguments(newArgs)
e.deathMessage(component)

I've tried implementing it like this where I create a new set of arguments but it seems that setting with component.arguments() doesn't actually change anything? Am I setting the arguments wrong?

sterile rampart
#

you're discarding the modified component

cloud vapor
#

check your ide warnings, components are immutable

night niche
#

the only warning I had was not making use of it

#

oh ok no I see what's wrong now

#

the component is immutable like said, but I imagine doing the casting fucked with ide warnings or whatever so I had no clue that calling .arguments() would not do shit.

#

but just making a new one worked

cloud vapor
#

heck yea

#

as? is your friend as well, you could

val component = e.deathMessage() as? TranslatableComponent ?: run { error; return; }
sterile rampart
#

you could just do val newComponent = component.arguments(newArgs) lol

night niche
#

ohh

#

its the "for this component" that tripped me up making me think it modifies the instance, but yeah returns a copy ig

sterile rampart
#

JB can't even use their own annotations for IDE warnings

#

typical

night niche
#

oh wait yeah contract pure

#

I've been spending too much time in oop java land at work lmao

#

Thanks a lot for the help, been very useful :D

undone cypress
#

Thanks for the help guys :D

sterile rampart
#

with all the time you're wasting writing messages on discord, you could have gathered data that could lead to improvements

sterile rampart
#

this is more or less the profiler you want to use: https://github.com/async-profiler/async-profiler, spark can use it as its profiler backend if you're on a platform that supports it. if you're running the latest adventure version and can get a profile from that, there's a chance it could have actionable results

#

anything else is a waste of time

#

PR?

#

or share the profiles so other people can use them

final shale
#

Why does this not play any sound?:

Sound testSound = Sound.sound(Key.key("item_flintandsteel_use.1"), Sound.Source.AMBIENT, 1f, 1f);
player.playSound(testSound, Sound.Emitter.self());
#

It throws exception error if I put in an invalid sound ID, so I know the code got executed, but I don't hear any sound.

final shale
#

Java Purpur

#

1.20.6

cloud vapor
#

and just to make sure, do you have the ambient slider turned up?

final shale
#

yes everything is maxed out, i don't think game settings is the problem

#

i tried all source type too

sand drift
#

_ and not .?

#

item.flintandsteel.use is how mojang refers to that sound, albiet 1.21.5

cloud vapor
#

No noticeable delays anywhere when it comes to text rendering, no. In regards to your "what do I do with the data" question, send it here! Complaining about issues doesn't help anyone, but giving us workable data that we can look into means we can make performance improvements that will help everyone 💪 For example, we had made multiple performance improvements in 4.20.0 because of spark reports from people in this channel! Help us to help you :D

final shale
#

That was the case. Thanks all!

marsh oar
#

is ~22mb many for just each sign ?

#

I saw geyser uses Adventure Renderer for Signs to translate content

sterile star
marsh oar
#

Just normal survival one

sterile star
#

Jesus

marsh oar
#

Idk if that one or many but from my opinion is already to much

sterile star
#

Yeah that is way way way too much

marsh oar
#

I fixed already 28mb memory allocation for just a replaceAll call

sterile star
#

I sadly do not know where your debugger is getting those numbers from, so i cannot really tell you what is up with that

#

That also seems like a really generic method which probably isn't tied to only signs (unless it is in the Sign class), so yeah

marsh oar
#

Sign allocated the most one

sterile star
#

Again, I do not know where the numbers are coming from. You might probably want to provide a bit more information

cloud vapor
#

Yeah you'll need to provide the full report for it to be at all useful to us

#

Worth noting that doesn't mean it's using 55mb in one call lol

quaint monolith
#

It is an immutable API, there will be lots of allocations

rare sage
#

yeah but not 55 MB of components lol

marsh oar
cloud vapor
#

Yeah I can take a look at a hprof tomorrow

marsh oar
marsh oar
cloud vapor
#

tbh im not really sure what your issues are here, nothing here looks problematic

marsh oar
#

The style allocates many memory from my opinion

cloud vapor
#

The software you're running is doing lots of style work, therefore it is gonna use a decent chunk of memory 🤷

#

There's a slight improvement to be made where the AbstractComponentBuilder could probably not create the style builder if the merging isn't going to do anything

#

but that's certainly a micro-optimisation

cedar birch
#

Is there some mutable form of audiences

quaint monolith
#

Audiences is a helper class with a single static method, what do you mean?

#

Audience is an interface, whether it's mutable or not depends on the platform integration layer

#

For Paper you have Player and Server implementing Audience and both are mutable

cedar birch
#

is there no audience.remove or audience.add

quaint monolith
#

Audience isn't a collection?

cedar birch
#

its a grouping of a viewers

quaint monolith
#

It's an interface

sweet hornet
#

You can make an implementation of ForwardingAudience wrapping a group of Audiences where you can add or remove them as will

#

In fact, the ForwardingAudience was designed for that kind of case (well, not specifically for the mutability, but for the "group of Audiences")

quaint monolith
cedar birch
#

i just needed something like this

class MyAudience : ForwardingAudience, HashSet<Audience>() {
    override fun audiences(): MutableIterable<Audience> {
        return this
    }
}
prisma mason
#

you can use Audience#filterAudience()

cloud vapor
#

that's not at all mutable audience

cloud vapor
#

I'm a bit confused about what you're actually asking for here? What do you mean "in a tree"? The point of a TagResolver is that it turns a name+arguments into the Tag instance that is inserted into the resulting component content, I'm not sure how a tree would make sense in this context?

#

Could you explain in a bit more detail what you're actually talking about? Possibly with some examples of how this would simplify code?

rare sage
#

ngl i have no idea what you're tryna say

quaint monolith
#

All of it

#

Are you saying adventure should have a new TagComponent and MM should parse into a tree with these new components then walk the tree afterward and resolve them?

cloud vapor
#

MiniMessage already parses the string into a tree IR. This is dependent on tag names because it allows us to ignore tag-like text that isn't a tag. Once the first pass is done to construct the tree, we then turn it into a component using TagResolvers to create the component/etc content.

If I understand correctly, you want us to ignore the check for tag resolver names and instead turn everything into a tag during the IR stage. Why? I can't see what this would improve, or what additional functionality this would let us add.

sly hull
#

I have a pretty newbie question regarding adventure.

I assume it is already shipped with the paper.jar?
And if so, should I just use it in maven with <scope>provided</scope> and can I work with the default docs, or are things like MiniMessage serializer and stuff all separate?

sweet hornet
#

They are separate modules, but all of them (or at least, all you should need) is already bundled with Paper yes

sly hull
#

Amazing, then I'll get started on the docs. Seems pretty straightforward, looks better than my hacky ChatUtil class lol
Thanks!

sly hull
#

Does this &6 also count as legacy? Or only the § prefixes?

cloud vapor
#

Both are legacy

sweet hornet
sweet hornet
cosmic loom
#

I'd argue NBT can be useful often

cloud vapor
#

This is not a "sea of new features", it's an idea about making the API more complex for reasons that you haven't yet stated. If you have ideas about why you want access to the entire tree structure then we can look at that and discuss what it may mean for the API, or if there are other, more simple ways we can achieve the same goals.

cloud vapor
#

It doesn't seem simpler to me, no. We'd need to be able to stabilise and expose the IR tree representation to developers which would limit the amount of changes we can do internally. I also don't see how it makes anything more concise. We'd need to provide the ability to mutate the tree as a whole which is already complex enough in Modifying tags. What is nice about the current API is that we can mostly do simple node to component replacements when we go from the IR to components. In a world where we expose the IR, we'd be adding an additional node to node step before the final node to component step. Again, I don't see what capabilities this would unlock anyway. Please do say if you have ideas about how you would benefit from such a change - it's always best to start with ideal features and then work backwards to an implementation.

sand drift
#

Because once you expose the internal tree it becomes an aspect of the API behaviour which limits ability to change or expand on the trees behaviour without breaking plugins

sand drift
#

I mean, lets say mojang does something, and we need to change the IR tree; congrats, you've now potentially broken every plugin using that mechanism

#

I can't imagine all the potential issues that could arise from changing the IR tree; it's not something that was designed to be exposed. there is very limited consideration to the tree being a stable system that can be exposed for 10 years with limited behavioral changes; Such is the joys of trying to design API which doesn't break every year when mojang changes something with how components work

sand drift
#

and MM has to update to reflect changes to mojangs component system

#

if mojang adds a new component, the tree representation may need to change internally to reflect any changes required to parse it, etc

#

hopefully that doesn't occur, but, if you expose something as part of the API, it becomes versioned with the API, and much harder to change should the need arise

#

the reference to the DOM tree is moot because the only consumer of the DOM tree that matters is the site and its frameworks, and the DOM tree is pretty darn stable to my understanding, it's not going to change its behavior in a point release because we needed to change how the tree works in order to represent some new concept or whatever

#

the only equivilent to the DOM tree would be 3rd party userscripts accessing the sites tree and being upset because the site made some UI change and broke their userscript, which is generally not a care website owners make, because their sites layout is not API; the DOM api itself is pretty stable outside of new additions to things on the tree nodes, the tree itself is unlikely to ever change

sand drift
#

We don't know what might need to change

#

Thays the entire point, we have not made the level of care to ensure that this format is unlikely to change in the chance that something needs to change with minimessage

#

Its not that it might need to change or not, its the “we have not done the work to assure that the IR tree and the parser wont need to change”, this is already like the 10th iteration of the parser, however

#

Exposing it requires ensuring that we have a documented standard of how it should work, and it would be much easier to expose a further intermediatary post tree so that were not worried about not being ever able to change the tag parser

#

Unoess you're talking about some other level and were all talking about different things

cloud vapor
#

Going back to your earlier point, it is incorrect to say that an API is simplified by adding more functionality. That is the exact opposite of what would happen. More API means more docs needed, means more semver applicable maintenance, means more areas to provide support for, etc.

You say you want to create a tag that modifies the parent component. That is not a feature request, that is functionality. I am asking you to provide examples of why you want that functionality so that we may better understand why you are asking for this. You also state that you want to define text inside of a tag, that is exactly what Modifying tags do (see the gradient tags for an example).

I do understand what you are talking about, but I am still yet to understand why you want this change. I think it would be helpful if you took a step back and provided some examples of things that you want to do, that you believe you currently can't. That way we can be on the same page about what you're trying to achieve, and we can all work together to the best solution, be that exposing more of the tree to tags or some other solution.

sweet hornet
#

I may have a usecase that could serve his purpose, and anyway if it's not related that's still something I had in mind few days ago but discarded as I was pretty sure it wasn't possible now (but maybe modifying tags could do it, on second though)

I have a MM string made with some texts coming from various places (I have custom tags that resolves to the custom name of an itemstack, or an entity, as well as static styled text). It is shown in an hologram (text display), but I wanted to darken (or lighten, or desaturate... we can imagine all kind of operation) some part of it. I ended up doing that manually in the static text, and leaving the dynamic texts untouched, but that's not a proper solution. To illustrate my goal:

Actual MM string (simplified):

<header:'<b>Help</b>'>
<gray>Right click with <crate-key:Vote> to <green>open</green> the crate
<gray>Talk to <npc:key-master> to <green>learn</green> more!
<footer>

Resolving to (there are colors in the key and NPC name as you can imagine):

=-=-=-=-=<[ Help ]>=-=-=-=-=
Right click with Vote key to open the crate
Talk to Key master to learn more!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

But I wasn't happy with the result, because "Talk to Key master to learn more!" was too much visible (whereas the main info was the other part of the text). So I would have use something like:

<darken:0.5><gray>Talk to <npc:key-master> to <green>learn</green> more!</darken>

But it does not exist. So I ended up using hex colors manually: [#696969](/guild/289587909051416579/channel/696969/)Talk to <npc:key-master> to <#0d5600>learn</#0d5600> more!, but the NPC name color is unaffected

cloud vapor
#

You could do that really easily with a modifying tag, simply just implement the apply method to just darken the colour on every component that you encounter

sweet hornet
#

That's cool then, I never used the modifying tag (always found it a bit complicated while I needed simple things). Do you know where I can find an example of such tags plz?

cloud vapor
#

This is actually a good example of why it's always best to start discussions with your end goal in mind! It's very easy to overlook or miss a feature that you weren't aware of, and starting with the end goal means we might not even have to spend time thinking about potential implementation changes 💪

cloud vapor
sweet hornet
#

Does it give me them recursively or do I need to do it myself?

#

To deeply apply the darkening

cloud vapor
#

You still haven't provided any real-world use case for this change in API. Your two code examples are indecipherable and I have no clue how they would be used by actual developers. Your initial text example with the message tag also has the same problem, I have absolutely no clue why anyone would want to do that, and I can think of almost a dozen saner alternatives (e.g. two separate tags that the end user can use with different styling).

#

I'm not going to keep engaging in this conversation unless you take a step back and explain why you need this API with proper real-world examples with context.

rare sage
#

new features are designed around needs, trying to come up with a solution for a problem that does not exist or has no usages is not really it

cloud vapor
#

There is also no universe in which the code examples you provided are "simplifying tag handlers".

rare sage
#

and if I am understanding your example correctly, even if you absolutely needed such low-level capabilities, you could also do some post-processing step that walks through the n.k.a.t.Component tree to do whatever, no?

#

and, again, what is that need?

#

why is this needed?

sweet hornet
cloud vapor
#

You have given examples of functionality you need. You have given zero examples of real-world problems that functionality would solve.

sweet hornet
#

You explained multiple times that you wanted a screwdriver, yes. But why do you need it for? What final goal do you want to achieve with it?

cloud vapor
#

yeah exactly, i don't want to go out and buy a screwdriver for you if all you actually need is to borrow my hammer

sweet hornet
sly hull
#
    public static void sendMiniMessage(CommandSender sender, String configKey, Map<String,String> replacements) {
        List<String> lines = language.getStringList(configKey);
        if (lines.isEmpty()) return;

        BukkitAudiences audiences = MegaClans.getPlugin().adventure();
        TagResolver papi = (sender instanceof Player p) ? papiTag(p) : TagResolver.empty();

        for (String tpl : lines) {
            String raw = tpl;

            // {key} → <key>
            for (var e : replacements.entrySet()) {
                raw = raw.replace("{" + e.getKey() + "}", "<" + e.getKey() + ">");
            }

            // %papi_foo% → <papi:"papi_foo">
            raw = raw.replaceAll("%([^%]+)%", "<papi:\"$1\">");

            // &#FFA500 → <#FFA500>
            raw = raw.replaceAll("&#([0-9A-Fa-f]{6})", "<#$1>");

            // deserialize with the MiniMessage tags + your PAPI tags
            Component comp = MiniMessage.miniMessage().deserialize(raw, TagResolver.standard(), papi);

            audiences.sender(sender).sendMessage(comp);
        }
    }```Why does it not pares the `<#FFFFFF>Test`? It keeps sending the raw message to the player. Also `papiTag(p)` is from the wiki FAQ
#

Tried myself and then with GPT but I feel like GPT doesn't know much about the api

cloud vapor
#

I don't see what this has to do with the api - parsing colour codes works fine for me

sly hull
#

This is what happens

cloud vapor
#

You don't need to parse the standard tag resolver to the deserialize call, the global minimessage instance already contains that and the additional tag resolver arguments in that function are additive to the ones in the minimessage instance

sly hull
#

This is where I am using the Method:

                if (cp.getClanId() == null) {
                    MessageManager.sendMiniMessage(sender, "Commands.info.Output.NoClan.Self");
                    return;
                }```The config part looks like this:
```yaml
    Output:
      NoClan:
        Self:
        - '%megaclans_prefix% %megaclans_textcolor%You are %megaclans_highlightcolor%not
          %megaclans_textcolor%in a clan'```I guess it is an issue with the placeholders? Not sure
cloud vapor
#

lead with the fact your papi tag is inserting the colours next time :p

sly hull
#

Yee forgot to mention, sorry

cloud vapor
#

the papi tag we provide an example for in the docs uses legacy formatting to produce the components, not minimessage

sly hull
#

Oh, so I have to write my?

cloud vapor
#

you'd need to make your papi tag use minimessage instead of the legacy serializer

sly hull
#

So changing this line

final Component componentPlaceholder = LegacyComponentSerializer.legacySection().deserialize(parsedPlaceholder);
#

Would do the trick?

cloud vapor
#

yes

sly hull
#

Like that I assume?
final Component test = MiniMessage.miniMessage().deserialize(parsedPlaceholder);

cloud vapor
#

yes

sly hull
#

Thanks a lot! :D

cloud vapor
#

🫡

sweet hornet
#

Are you sure it works as you intend? 👀

sly hull
#

Yep just realized

#

Only parses the first colors

#

Then it uses default white

sweet hornet
#

I think you rather need your papi tag to insert the raw string with Tag.parsed(...)

cloud vapor
#

right yeah, if you want the colour to "bleed" into other tags, components, etc

#

alternatively you could swap them for a styling tag if you know they are colour codes

sly hull
#

I am not sure where to handle this, because the Tag object doesn't seem to have a parsed method

#

I really gotta learn more about adventure 😵‍💫

cloud vapor
#

its preProcessParsed

sweet hornet
#

Then let the rest of your MM instance do the parsing. However, do note that the custom replacements you apply (%placeholder% to <papi:"placeholder">, {var} to <var> and legacy &#123456 to [#123456](/guild/289587909051416579/channel/123456/)) are probably not applied recursively to the content of the papi values

sweet hornet
sly hull
#

So I need to run the code as often until everything is resolved?

cloud vapor
#

no that's not how it works unfortunately

sweet hornet
#

I'd suggest you moving that part in a dedicated function and calling it once on your initial text, and also in the papi tag before using Tag.preProcessParsed

sly hull
#
    public static void sendMiniMessage(CommandSender sender, String configKey, Map<String,String> replacements) {
        List<String> lines = language.getStringList(configKey);
        if (lines.isEmpty()) return;

        BukkitAudiences audiences = MegaClans.getPlugin().adventure();
        TagResolver papi = (sender instanceof Player p) ? papiTag(p) : TagResolver.empty();

        for (String tpl : lines) {
            String raw = tpl;

            // {key} → <key>
            for (var e : replacements.entrySet()) {
                raw = raw.replace("{" + e.getKey() + "}", "<" + e.getKey() + ">");
            }

            // %papi_foo% → <papi:"papi_foo">
            raw = raw.replaceAll("%([^%]+)%", "<papi:\"$1\">");

            // &#FFA500 → <#FFA500>
            raw = raw.replaceAll("&#([0-9A-Fa-f]{6})", "<#$1>");

            // deserialize with the MiniMessage tags + your PAPI tags
            Component comp = MiniMessage.miniMessage().deserialize(raw, papi);

            audiences.sender(sender).sendMessage(comp);
        }
    }```Is this even the best approach to translate legacy? Or should I use the `LegacyComponent..`
sweet hornet
#

OR, surely Kezz know better, but I think there's a notion of text pre-processing and component post-processing in MM

sly hull
#

So I need to preprocess the whole message until everything is "parsed", and then with a helper method I'll send out the message?

sweet hornet
#

And do the migration once for all

sly hull
#

Ye, I'll just put minimessage in the default config

cloud vapor
#

yeah agreed

#

it's a lot easier to use one format everywhere

sly hull
#

Legacy is still so widely spread among other plugins, it's crazy

#

Gotta start becoming a part of the solution not the problem lol

cloud vapor
#

heck yea that's the attitude to have 💪

#

you'll save yourself a lot of hassle that's for sure

sly hull
#

I think so too! :D

Thanks for the help guys! I think I'll come here more than this one time since I am very new to the Adventure Framework

cloud vapor
#

you're very welcome to!

sly hull
#

Is there also a clean way to send to console in the adventure api?

midnight spire
#

Plugin#getComponentLogger

#

(if you are asking for Paper lol)

sly hull
#

Still using Spigot api 😭

midnight spire
#

adventure itself does not have a console

#

then that question would be more related to adventure-platform, not the api

umbral wharf
#

sent this elsewhere already but sending it here so its seen by more people, a possible feature to be added now we have the localization stuff is adding pointer's to translatable component methods been as it already takes tag resolvers just doesnt take pointers yet

#

might be a bit of a niche addition but useful non the less

cloud vapor
#

maybe you could explain what youre trying to achieve and then we can point you in the right direction

umbral wharf
cloud vapor
umbral wharf
#

Ahh

#

Tyty

cloud vapor
#

happy to help! 🫡

sly hull
#

What about servers with ViaVersion installed (bad practice, I know, but many servers do that).
Does ViaVersion translate those Components for legacy or would I need to handle that myself?

And things like shadow will probably not even work on previous versions, right?

last horizon
#

viaversion translates components to the legacy equivalent
hex colors will be replaced with the closest legacy color

sly hull
#

Oh awesome, thanks

last horizon
#

And things like shadow will probably not even work on previous versions, right?
havent tried it but i bet it just omits the shadow

grim bear
#

in MM, is there a kind of conditional tag? meaning if a condition is true, show one thing, and if it's false, show something else instead?

sweet hornet
grim bear
#

I'm using a third-party plugin which uses MiniMessage formatting, so I don't think they apply to my case unfortunately

cloud vapor
#

MiniMessage does not have any in-built tags like that no

sweet hornet
#

How would your condition be evaluated then?

grim bear
#

from a placeholder

#

it's fine though, it's gonna be a placeholder from my own plugin, so I can make it return whatever. just thought it would be neater if it returned "true" or "false", and then MiniMessage could display something else depending on which is the case

sweet hornet
#

If you can't control the code behind, I doubt it'll be possible to do such a thing

#

You can add a if tag, but you'll need to be able to pass the tag resolver so your MM string is deserialized with that tag

#

Maybe one day we'll have scoped tag resolvers that plugins can register in the default MM instance

cloud vapor
#

it's next up on my big minimessage todo list

sweet hornet
#

I must say it's kind of a long awaited feature on my side 🙂

cloud vapor
#

still need to decide what the best separator is for namespace vs name

hardy crest
sweet hornet
#

I feel that the : for separating name and args is not the best

#

It would better fit the namespaced name like everywhere else

cloud vapor
#

annoyingly we can't do : bc it separates arguments

#

im probably leaning towards namespace/name

sweet hornet
#

And having idk maybe whitespaces for separating name and arguments

#

And then allowing named arguments

cloud vapor
#

yeah named arguments are v easy to fit in our "spec"

sweet hornet
#

Well, there's a little bit much to think about, but I deeply think that kind of change IS the opportunity to do so

cloud vapor
#

just name argname=value

grand knoll
#

or is it not for vanilla keys / has a different allowed path character set

sweet hornet
#

We're only talking about the MM tag resolver names

grand knoll
#

ah ok

sweet hornet
#

I doubt / is an actual allowed character in such place rn

cloud vapor
#

yeah sub/path isn't a valid tag name

last horizon
#

for automated tests do i have to implement my own translator or is there something that works without any platform specific code

#

because the global translator has no sources and always returns null when translating

sterile rampart
#

well do you register anything wjth the translator?

last horizon
#

nvm i add a source true

#

okay no the sources arent empty my test was wrong
but the minimessage translator always returns null

#

i am using translate(String,Locale)
the way i see it there is no impl for that method tho
just always returns null

#

turns out i am just stupid
my error handling didnt print anything for some reason
the translation store is just empty

last horizon
#

i seriously cant get it to work
the translate method always returns null
as seen in the debugger the translator contains my translations
what am i missing

sweet hornet
#

I never used the adventure translation system but this looks like you're using the wrong method?

last horizon
#

i didnt read the docs

#

using TranslatableComponent instead of translation key works
but thats weird sht ngl

sweet hornet
#

The String key => MessageFormat is for string-based translation while TranslatableComponent component => Component is for component-based translation, if I understand that doc well: https://docs.advntr.dev/localization.html#using-a-custom-translator

last horizon
#

this method is called only if translate(TranslatableComponent, Locale) returns null.
thats weird

cloud vapor
#

not really, how else are we gonna know what method to use

#

the MiniMessage translation system works with components obvs, so you need to give it components

gritty tundra
#

is there any way to make a translatable component into lowercase? for example block.minecraft.stone gives me Stone but I'd prefer stone, although I realize this is language dependant

sterile rampart
#

no

gritty tundra
#

whats the proper terminology for the middle segment of a translation key, aka the minecraft part in block.minecraft.stone. is that called a namespace? I assume so since namespacedkeys

cloud vapor
#

nope, they're just all strings

grand knoll
#

I mean technically that is a namespace, of the block, but it's just string concatenation / it doesn't have any special meaning in the translation key

last horizon
#

how do i access translations from the global translator with minimessage?
i have a test translation called hello and using <lang:hello> just prints the tag
using .translate on the global translator works but i cant add arguments that way?

#

wait... does component translation even work without a platform?

sweet hornet
#

<lang:hello:arg1:arg2…>?

last horizon
#

yes with minimessage i know i meant i cant add arguments to already translated components
described that a bit weird

umbral wharf
#

if you use Component.translatable with the string key you can provide arguments on that

last horizon
#

i know but that doesnt work for some reason
i am using a minimessage translation store

#

hello=Hello there %s!
this is my message for reference

cloud vapor
#

Did you add your translation store to the global translator?

umbral wharf
#

^^

last horizon
#

yes

cloud vapor
last horizon
#

i mean Component.translatable("hello", Component.text("NonSwag"))
on translate

cloud vapor
#

yes, read the docs :p

last horizon
#

oh okay thanks
but still using minimessage only yields the tag itself

#

is that platform specific?

umbral wharf
#

whats your updated code now

cloud vapor
#

yeah it'll be good to see the context in which you're sending the component

last horizon
cloud vapor
#

what platform?

last horizon
#

none

#

thats why i am asking whether the minimessage thing is platform dependent

cloud vapor
#

okay well two things, first that component logger line isn't how you use minimessage's translation system, you need to use the translatable component as you've done above

#

secondly, i don't think the component logger runs things through the global translator by default, good issue to open though as it probably should

#

oh no it definitely does, are you providing your own component logger, overriding the default impl?

last horizon
#

no i just checked

#

i previously had some issues related to that but those are gone

#

btw thats the output
i think i explained that a bit weird on what is happening

cloud vapor
#

lead with that :p

last horizon
#

thats why i tried placeholder

#

doesnt work with that either

cloud vapor
#

your issue is you're not using the minimessage translation system correctly in your component logger example

#

build the translatable component normally, as per the docs

#

the docs are your friend

last horizon
#

thats not right xD?

last horizon
#

i am on 4.20.0
latest release

umbral wharf
#

ah appears that was added changed after that release

cloud vapor
#

yeah we haven't released 4.21.0 yet

last horizon
umbral wharf
#

use Component.translatable not minimessage deserialize

#

and afaik it wont parse tags inside of tags so either use the key in that or pass args into the lang:xyz

last horizon
#

i just want to confirm if that even works thats all

last horizon
umbral wharf
#

to me it looks like you only have lang:hello not <lang:hello:argument>

umbral wharf
#

stuff passed into a tag will be unnamed, use arg:0 iirc for those

last horizon
#

omg thank you

#

finally

#

that took way too long

cloud vapor
#

is there something i can do to make the docs clearer? i thought I'd explained it all pretty well in there :/

umbral wharf
#

id say its pretty clear to me

last horizon
#

i thought that is just for use with Component#translatable and not the mm tag

cloud vapor
#

the lang mm tag is a translatable component

#

all your code is doing there is a roundabout way of creating a translatable component

last horizon
#

but to be fair i suck at reading docs
for that reason i mostly just skip through the code until i get what's happening

#

instead of a one day project that became a two day journey

cloud vapor
#

well next time make sure to read them clearly :p otherwise we're just basically reading them for you

last horizon
#

i did, i just dont get them
doesnt have to be because they arent written well

gritty tundra
#

does LegacyComponentSerializer provide any method to detect which character is being used? I can do myString.contains(LegacyComponentSerializer.AMPERSAND_CHAR) but that's technically not selective enough

wet kestrel
#

What do you mean to detect?

gritty tundra
#

figure out which character the string is using, because I'm stripping colors from text provided by other sources so I don't know what they used

wet kestrel
#

So you're starting with a string, without knowledge of what it was encoded with, and hoping you can identify if it's likely using section signs, ampersands, or something even more cursed?

gritty tundra
#

exactly

wet kestrel
#

There's no way to be certain.

gritty tundra
#

luckily this is only affects a very very small number of users of my plugin so if it's not accurate... people will survive. I'll either use contains and hope for the best or just pick the section one

wet kestrel
#

I feel like you're dealing with a bad design issue. You shouldn't be getting random text from other sources like that. 😆

gritty tundra
#

aint that the truth. just my eagerness to accommodate everyone

wet kestrel
#

Accept only minimessage and move on. rgb_shrug

gritty tundra
#

in this case I'm purely stripping the color/format codes because the text is all I care about

last horizon
#

is there any guarantee that Component.empty() always equals MiniMessage.miniMessage().serialize("")?

cloud vapor
#

should be yeah

graceful kestrel
#

okay so apparently you do not want to return "" in MiniMessageTranslator#getMiniMessageString.......

cloud vapor
#

because...?

#

oh

#

yeah

#

lol

#

returns null :p

graceful kestrel
#

it took me a while to figure that out

cloud vapor
#

that is a funny one

near flame
#

Hey! I am trying to use click:open_url:'url' tag. When sending a message containing a link deserialized using MiniMessage.miniMessage().deserialize, its not clickable. I get the message from a config using the ``YamlConfiguration getConfig().getString()` method. Does anyone know the issue?

graceful kestrel
#

what string are you providing

fiery oracle
#

what platform are you on?

near flame
#

I figured it out, forgot a character

near flame
#

Is there a way to center a component in chat?

fiery oracle
#

no

#

players can change their chat width in settings

near flame
#

I mean yeah, but there is a method for regular strings, but it doesnt work for components

sweet hornet
#

What method are you talking about?

small cloud
#

I've been away for a while, and i know that if a monster is near the bed there used to be a message sent from the server of the type game info or something similar.. can i send such a message to the client? how do i send such a message, i am explicitly not using the proper send action bar for testing purposes

prisma mason
#

and do you mean translations?

small cloud
small cloud
prisma mason
#

well minecraft has lang files for their messages if you want localized messages

#

aka, translated to the clien't locale

small cloud
#

i don't mean localized messages i mean the act of sending a message to the client there exists player.sendactionbar but it's there some way to send the bed type message something like player.sendgameinfomessage or whatever

prisma mason
#

the bed message is an action bar message?

small cloud
#

at least 3 years ago they were different, so that's why I'm asking if they still are or aren't

prisma mason
#

not that I know of, but atm Player implements Audience which you should use

small cloud
#

ye ye i know that much

small cloud
#

also just tested, they are different, they are a system chat message with the overlay parameter set to true

last horizon
#

why doesnt the console render translatable components properly on paper?

#

sometimes even the raw translation key is displayed
for example when using translatable component in join or quit event

near flame
#

There are ways to center a message, but it's not intended for components

sweet hornet
#

That code won't work if the client resized its chat

near flame
#

Are there any ways to send a player a centered component?

near flame
sweet hornet
#

You can apply the same hazardous logic

near flame
sweet hornet
#

Either you failed, or we cannot do better

#

It depends too much on your context

#

Components are so much more powerful than simple text

#

So it comes with the limitation that you cannot accurately measure its width

near flame
#

I see, thanks for clarifying

sweet hornet
#

If your components only contains simple text, then you can use the ComponentFlattener to try to measure its length, and then pad with some spaces

primal dome
#

We need <pink> as color in the next update 😭 Hat to always search a hex

sweet hornet
#

Isn't <light_purple> pink?

primal dome
#

THERE IS LIGHT PURPLE?

#

WHY not just pink

#

welp

#

Didnt know that

#

Thx

sweet hornet
#

Ask mojang

primal dome
#

ohh, thats the way they do it

prisma mason
#

You could also use hex colors

#

Or just add your own tag resolver

near bolt
#

Hi there, is that normal the lang tag does not seem to work with item stacks, when using a custom MiniMessage translator?

wet kestrel
near bolt
#

using messages for instance it works fine:

near bolt
#

but in items it just shows the plain key. Also, the getMiniMessageString of my translator does not get called at all.
-# of course, I set the display name as lang:KEY and not the key itself; it seems mini message just parses it as the key as no translation is found in minecraft's vanilla bundle

grand knoll
#

I believe items don't use the global translator, because different languages would have different items nbts etc

#

So they use vanilla client side translations

near bolt
#

how would I do that?

#

ah

#

you mean it's not possible at all?

#

I'll have to handle translation myself basically?

#

e.g. based on the player's local, get the right key of my custom store ...

grand knoll
#

Not without a resource pack I dont think, but I'm not 100% sure

near bolt
#

imma do some researches, thanks !

grand knoll
#

Btw what platform is this on?

near bolt
#

wym plateform? server type?

#

I'm using paper 1.21.4

grand knoll
#

You might have better luck in #paper-dev then
As adventure is more for the API instead of the integration on where it's used.

near bolt
#

Oh oki, as it was directly related to MiniMessage's trnaslation system I though this was the right channel sorry 😅

grand knoll
#

Nah it's fine

near bolt
#

I'll make my own system anyway, from some resaerches on neofgre's forums it does not seems possible 😦

#

thanks for the help tho love_rainbow

grand knoll
#

The big issue with sending different items internally iirc is creative mode, since they copy the item as-is

cloud vapor
#

Correct, on Paper the GlobalTranslator is not used for items

grand knoll
#

So if packet tricks are used to translate it, they would copy it with the translated, not the translatable name/lore

cloud vapor
#

You'll need to just manually translate the name/lore yourself, but bear in mind that they will then be different items in different languages, so it's best for those to be used for things like GUIs where the items aren't persistent

#

Otherwise resource packs are a good shout

near bolt
#

my main goal was using that translation system with GUI, which I would have done myself before knowing global translators wouldn't work xD

last horizon
grand knoll
grand knoll
#

Oh sorry I see what you mean

#

Added the translated string as a fallback would still modify the item

near bolt
#

Ah while i'm here, I tried doing my own translatable component, how would I make it per-player then?
The tag resolver offer a context but not the audience itself, what would be the best way to handle that?
-# don't hesitate to say if i'm in the wrong channel now, not where I should go for MM as Kyori's dsicord have their channels closed

last horizon
last horizon
grand knoll
last horizon
#

oh no it would nvm

near bolt
#

ah yea didn't think about that

grand knoll
last horizon
#

just disable creative mode
easy fix

grand knoll
#

Iirc fabric's serverside modding framework polymer does something very similar, since it modifies the codecs to change modded items into a vanilla item representation when it is serialised for the network

near bolt
#

maybe it's a dumb idea, but can't we play with the new stack component to let multiple items stack anyway? although they are different in their components?

grand knoll
#

If an item stack has different components, they won't stack

#

All items in a stack need to be fungible/equivalent iirc

#

Iirc the stack component is just for specifying an alternative max stack size

#

I wonder if paper could have an API to opt in to having items affected by the global translator on a per item stack basic

IE
This is in a temp gui only, it's fine

The players will never be in creative mode / whatever, they can't get ruined that way

From thinking, it would probably have to set some kind of flag in the nbt/custom data component to say it's globally translatable / ok to be modified per-player to prevent the case where a player has an otherwise equivalent item to the resolved item, and try to stack it.

#

Idk

near bolt
#

Hi there, me again 😂

I'm tring to make my own translate/lang component. Here's how I edited the tag resolvers:

builder.tag("tw-lang", (args, context) -> {
    final var key = args.popOr("The <tw-lang> tag requires exactly one argument, the key you want to translate.").value();
    final var rawArgs = new ArrayList<String>();
    while (args.hasNext())
        rawArgs.add(args.popOr("The <tw-lang> tag requires exactly one argument, the key you want to translate.").value());
    final var player = context.targetAsType(Player.class);
    final var locale = player.locale();
    final var value = langService.getTranslation(key, locale);
    if (value == null)
        return Tag.selfClosingInserting(Component.text("(not found) " + key));
    final var resolvers = new ArrayList<TagResolver>();
    int argIndex = 0;
    for (final var arg : rawArgs) {
        resolvers.add(Placeholder.parsed("<arg-" + argIndex + ">", arg));
        argIndex++;
    }
    return Tag.selfClosingInserting(context.deserialize(value, resolvers.toArray(new TagResolver[0])));
});

However, it does not seems to parse correctly:

cloud vapor
#

Is there any reason why you're making your own lang component?

near bolt
#

Yeaa but it does not work for items xD

#

that was the previous discussion 😂

#

I used that already, but it does not apply for items, thus I guess I wanted to make it myself

cloud vapor
#

No reason to make your own system though, you can manually translate components via the GlobalTranslator

near bolt
#

ah?

#

you mean I keep the tw-lang tag, but i'm using the global translator to handle format and args?

cloud vapor
#

GlobalTranslator.render(Component, Locale)

near bolt
#

Ohh hum, what would the component be?

cloud vapor
#

No, just use the global translator to render your components and use the existing MiniMessage translation system

near bolt
#

the key?

cloud vapor
#

Component.translatable("my.translation.key") or w/e

near bolt
#

yea but how would that work for items?

cloud vapor
near bolt
#

e.g. I just use <lang:my.custom.key>?

cloud vapor
#

No, you'd set the name/lore and then translate them manually using the global translator

near bolt
#

then sets the name to the translated component from the translator?

cloud vapor
#

Yes

near bolt
#

hum

#

it limits my ideas a bit x)

cloud vapor
#

So you just use the entire existing systems, without creating anything yourself - all you would do to make it work with items is just manually render the component using the global translator as needed

cloud vapor
#

it's a ridiciously flexible system

near bolt
#

My current item system does not work this way, as I may format things before the translated texts, e.g. changing the name to <accent>→ <shade-red:500><lang:key>, I'm always setting item names using that way

#

or I missed the point 😅

cloud vapor
#

I don't see what the issue is here, the GlobalTranslator renders an entire component, e.g. Component.text("hi", RED).append(Component.translatable("my key"))

#

doesn't matter what prefixes, suffixes or surrounding component parts you have

near bolt
#

so it does apply to MM? I'm only using MM, not creating componennts like your example

cloud vapor
#

MiniMessage creates components

near bolt
#

but doing <accent>→ <shade-red:500><lang:key> for instance does not work for items as I've tested

cloud vapor
#

so whether you're using MiniMessage to create the components or making them manually doesn't matter

#

Yes because you have to run the resulting component through the GlobalTranslator manually as I said earlier

near bolt
#

ahh got it

#

First I deserialize the raw text into component, then pass that componennt into the translator

#

so it parses it manually anyway

cloud vapor
#

I highly encourage you to give the docs I linked a thorough readover, they are very comprehensive

near bolt
#

Oh right, sorry sweatblob

#

Thanks for your help anyway! love_rainbow

cloud vapor
#

happy to help!

near bolt
#

I'm back :P
After reading all the docs and made some refector, I'm facing another issue; how would I make newline/br working in item's lore?
It works in chats (it puts the two part on two different lines), yet on item stacks it just shows the "plain" unicode chars. How can I fix/handle that?

cloud vapor
#

Lore doesn't support newlines, so you should be using a list of components

cold granite
#
    private fun loadInternationalization() {
        val store = TranslationStore.messageFormat(Key.key("abc:permissions"))
        val bundle = ResourceBundle.getBundle("language", Locale.US, UTF8ResourceBundleControl.get())
        store.registerAll(Locale.US, bundle, true)
        GlobalTranslator.translator().addSource(store)

        val mmstore = MiniMessageTranslationStore.create(Key.key("abc:mmpermissions"))
        val mmbundle = ResourceBundle.getBundle("mmlanguage", Locale.US, UTF8ResourceBundleControl.get())
        mmstore.registerAll(Locale.US, mmbundle, true)
        GlobalTranslator.translator().addSource(mmstore)
    }

    @EventHandler
    fun onJoin(event: PlayerJoinEvent) {
        if (!config.formatJoinQuitMessage) return

        event.joinMessage(
            Component.translatable(
                "message.join",
                NamedTextColor.GRAY,
                formatPlayer(event.player)
            )
        )
    }

Is this illegal? Because the message is not translated when I join the server.

  • "this" -> having multiple TranslationStores at the same time
last horizon
#

is message.join actually in the bundle?

cold granite
#

Sorry, I meant it's not translated into another language except US

last horizon
#

you dont have any other languages registered

cold granite
# last horizon you dont have any other languages registered

Ah damn now I get it. Thank you. I realized that TranslationRegistry is deprecated so I couldn't copy my code from a velocity plugin and just copied the code from the paper docs without really thinking about the registration o.O. My bad, thanks for being polite

cloud vapor
#

Check the docs I linked you earlier, they are very comprehensive

#

Nothing wrong with having multiple translation stores tho

cold granite
near bolt
#

Any reason (and how to prevent) using mini messages tags makes them raw with the lang tag?

#

e.g. I have a test key, sets as <red>Hello!, well using the lang tag, it shows in plain <red>Hello! without formatting the color

umbral wharf
#

did you create a custom minimessage instance? if yes you need to add the tags

near bolt
#

aren't they added by default?

#

and other tags seems to be working anyway

#

I end up with for instance

umbral wharf
#

add StandardTags.defaults() to your builder on #tags()

sour quarry
near bolt
#

while my name is this; toTranslatable simply surround the key within lang:XXX

#

I'm giving my trsnaltor a custom MM instance btw, the one I setup to work with those custom tags

sour quarry
#

Yeah, that's working as intended, translatable content doesn't contain formatting, handle that in the wrapping MM string

near bolt
#

Hum after some struggling, I found out I'm using placeholder tags when calling the deserialize of my MM. How can I make them also apply to the lang component?

cloud vapor
#

See the docs I linked

#

they have a section about adding custom tags

near bolt
#

ah yes yes, i've already added custom tags, but mine are "context-dependant" and I'm not sure how to implement these globally

#

All my three <text>, <base> and <accent> uses the given color scheme when formatting with my method

#
    @Override
    public @NotNull Component format(@NotNull String message, @Nullable Locale locale, @NotNull List<TextColor> scheme, @NotNull TagResolver... tagResolvers) {
        Checks.notNull(message, "Message cannot be null");
        Checks.notNull(scheme, "Scheme cannot be null");
        if (locale == null)
            locale = langService.getLanguage();

        // Check if the message contains any color overrides
        final var matcher = OVERRIDE_SCHEME_PATTERN.matcher(message);
        if (matcher.find()) {
            // Extract the color name from the message
            final var colorName = matcher.group(1);
            final var color = Colors.getSchemeByName(colorName);
            if (color == null)
                throw new IllegalArgumentException("Invalid color name: " + colorName);

            message = message.replace(matcher.group(0), "");
            scheme = color;
        }

        final var placeholders = List.of(
                Placeholder.styling("text", scheme.get(Colors.SHADE_200)),
                Placeholder.styling("base", scheme.get(Colors.SHADE_500)),
                Placeholder.styling("accent", scheme.get(Colors.SHADE_700))
        );
        final var placeholdersWithTagResolvers = new ArrayList<TagResolver>(placeholders);
        placeholdersWithTagResolvers.addAll(Arrays.asList(tagResolvers));

        final var parsed = MINI_MESSAGE.deserialize("<!i>" + message, TagResolver.resolver(placeholdersWithTagResolvers));
        return GlobalTranslator.render(parsed, locale);
    }
cloud vapor
#

Well you can set the target of the translation by adding Argument.target(player or some random object you make) which can be retrieved from your tag resolvers with context.target()

fossil timber
#

what in the backwards compability is this? 💀

sterile rampart
#

minecraft used to use that version

#

no point in upgrading it

fossil timber
#

Even with adding the library to plugin.yml and shading the gson library, the error persists

sterile rampart
#

how

#

the minecraft server has a certain version of gson, adventure doesn't change that

fossil timber
sterile rampart
#

no idea what voidgen is

fossil timber
sterile rampart
#

the classloader hierarchy just doesn't work like that

sterile rampart
#

you can't change that

fossil timber
#

ItemEdit downloads adventure as via plugin.yml

sterile rampart
#

and?

fossil timber
#

Which loads gson 2.8.0

sterile rampart
#

no

fossil timber
#

Look at the logs

sterile rampart
#

why? the logs don't say anything useful

fossil timber
#

Voidgen (my fork of the plugin) loads gson 2.13.0

sterile rampart
#

gson. is. in. a. parent. classloader

#

you cannot override that version

fossil timber
hardy crest
obsidian nimbus
#

Whats the point of JSONComponentSerializer? Gson wasnt sufficient?

cloud vapor
#

Allows you to not care about what backend serializer you're using

last horizon
#

is there a serializer that makes it possible to send components with com.mojang.brigadier.Message#getString?

#

platform independent preferably

cloud vapor
#

I mean, we have adventure-platform yeah

last horizon
#

i cant find anything related to Message
i want to create a command syntax exception with a component as message

mental sierra
#

Why doesn't Formatter work with Instants like this?

MiniMessage.miniMessage().deserialize(
    "Current date: <date:'yyyy-MM-dd HH:mm:ss'>!",
    Formatter.date(
        "date",
        Instant.ofEpochMilli(System.currentTimeMillis())
    )
);
last horizon
#

dont really know why but it only works with
LocalDateTime.ofInstant(time, ZoneId.systemDefault())
had to figure that out yesterday too
took an entire hour and a lot of broken keyboards /j

mental sierra
#

Thanks!

Instant.ofEpochMilli(millis).atZone(ZoneId.systemDefault())

also seems to be working, it probably just requires the zone data inside the TemporalAccessor. I think it would be helpful if this was documented, or at least if an exception was thrown, rather than it being a silent issue...

cloud vapor
#

Instants don't contain information needed to produce a date like that

last horizon
mental sierra
cloud vapor
sweet hornet
#

The problem is in Java itself, unfortunately. You can see the same issue with the DateFormat (or something like this) throwing an exception when given an Instant, despite accepting it

cloud vapor
#

There are other TemporalAccessor implementations that it does accept, e.g. ZonedDateTime, LocalDateTime, LocalTime, etc etc

#

The only "common" interface between them is TemporalAccessor

last horizon
gritty tundra
#

if I convert a component to plain text and it includes a translatable component, will that text be the translation key?

sterile rampart
#

depends on the flattener that you use

gritty tundra
#

I've never specified one so I suppose I use whatever the default is. which would I need to not get the translation key, or the english/default translation instead?

cloud vapor
#

The default depends on the platform you're using

gritty tundra
#

although tbh maybe it's easier to just build a string at the same time I build the component, because I store the string so I don't want it in a component format

#

it'd be either spigot or paper-based servers. not sure yet how long I'll support spigot

#

boy I sure wish I could show items in the chat lol

prisma mason
#

as a hover event? because thats built in

gritty tundra
#

not a hover event, directly inline like some mods do. with complete hover support to show the lore etc

#

just like you'd see in an inv

prisma mason
#

I never played with a mod like that

gritty tundra
#

I was only wishing I know it's not possible in paper etc. My issue is the translation question

cloud vapor
#

Paper will translate everything in the PlainTextSerializer yes

#

dunno about adventure-platform

gritty tundra
#

I don't think it does

#

the platform

sterile rampart
#

platform-mod will, I think the legacy platforms might if you're explicit abt the flattener

gritty tundra
#

which flattener should I be specifying then? I'm not familiar with which does what but I do see where to specify it

#

my goal is to get the text without any color/formatting/event/hover and translated text, not translation keys

cloud vapor
#

I don't think we ship one that translates by default

#

ig take a look at the paper one or w/e

#

or just update and use paper

gritty tundra
#

is minimessage in paper too?

sterile rampart
#

yes

gritty tundra
#

ok ya paper translated it by default. I can't remember them all but reasons to drop spigot support keep coming up so I might as well

fiery oracle
# last horizon is there a serializer that makes it possible to send components with `com.mojang...

not exactly sure what you mean by this, but you'd have to use that platform's mechanism for converting from an adventure component into a minecraft component (since that implements Message)
i.e. https://jd.advntr.dev/platform/mod-shared/6.3.0/net/kyori/adventure/platform/modcommon/MinecraftAudiences.html#asNative(net.kyori.adventure.text.Component) for modded platforms, https://jd.papermc.io/paper/1.21.5/io/papermc/paper/command/brigadier/MessageComponentSerializer.html for paper

umbral wharf
cloud vapor
#

TranslatableComponentRenderer is where you'd start

#

There is a PR open that badly fixes it by just rendering again and again until it's fixed, but the proper solution is to rework that renderer a bit to properly support component translation, it's a bit of an afterthought atm

umbral wharf
#

after a quick look i think that first part wouldve probably been my thought about it too

cloud vapor
#

yeah definitely, it would fix it, but a properly rerender self on any translate that could end up with more to render if that makes sense

umbral wharf
#

yeah it could def go down a really long rabbit hole on that

cloud vapor
#

yeah 😬

umbral wharf
#

only thing i could think of to not have that is an index of some form that limits the recursive, could be forced by adv or a param on a builder somewhere

umbral wharf
#

whats the easiest way to actually test translatable component renderer changes, tried a test and i dont think i know the right methods for it all and only other thing i can think of is just mavenlocal it and up paper to it and test with that

wicked torrent
#

you mean test in game?

#

probably includeBuild adventure in Paper or adventure-platform-mod

umbral wharf
#

test anywhere tbh, just to see what the renderer is actually creating

cloud vapor
#

yeah I'd just run a test, we should have tests for the renderer somewhere

umbral wharf
#

did some testing after adding stuff into renderTranslatable in Transl comp renderer and it just never actually gets called for any components being sent through the mm localization it just forwards it up the chain to the store so idk if i missed something but im slightly confused on where id actually even need to add the recursive to make it render transl args for it all

plush ginkgo
#

how do i replace a tag after it has been parsed ?

#

like i have this message "You have been given <number>", it is already parsed to component, but when i send it to the player i need to insert the value

#

how do i do it ?

#

through Component#replaceText() ?

sour totem
#

the idea behind tags is that you provide the value before parsing so that you don't have to replace some specific string afterwards like that

plush ginkgo
#

i am using ConfigLib to pre parse all minimessage string before hand because parsing them everytime i need to send them is not good for the server performance overall

#

i have tested like, parsing minimessage string on the fly in a player drop item event, checked spark and it took lots of ticks

sour totem
#

you're welcome to share that report if you still have it

#

parsing minimessage strings doesn't take long at all, and only causes you to have to do workarounds like these that'll probably take more cpu cycles than you're saving by pre-parsing

vivid bobcat
#

is there some tag like that in MiniMessage?
<lower>ASDASD</lower> -> asdasd
If not how can I create a tag like that?
I need it for uppercase, lowercase, capitalize, trim, etc

sweet hornet
#

That's interesting. For sure there isn't, but that's a good coding exercise to familiarize yourself with MM tag resolvers imo

vivid bobcat
#

so is it possible to create one?

sweet hornet
#

Definitely yes, using a Modifying tag

vivid bobcat
#

I'll take a look, thanks

sweet hornet
#

Let me know if you need further assistance, I think I managed to get a solution in a few lines of code, but I need to test it first

vivid bobcat
sweet hornet
#

Nop, you need to create your own implementation

#
public class TagToLower implements Modifying {
    @Override
    public Component apply(@NotNull Component current, int depth) {
        // Your logic here
    }
}
vivid bobcat
#

hmm ok

#

and what should I do with taht current component?

#

serialize to plain text?

sweet hornet
#

Probably not

#

But you need to think about how to handle different components

#

Easiest being TextComponents, you just get their text content and lower it

#

Then, also easy, most other components, you can't do anything to lower them

#

So just return them untouched