#adventure-help
1 messages · Page 3 of 1
Because we don't deconstruct and mangle items sent over the network for that
It wouldn't be a problem as is explained in the docs
I see
okay, seems like I will run into too many problems trying to implement localization.
thanks anyways ❤️
Not really, it's a really simple system
The only problem you'll have is translating the text
adventure handles everything else
I mean, yea, you're not just tossing raw strings around but need to formulate a source of strings coming from elsewhere
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.
and what im saying is that server side translations are incredibly easy
once you have the lang files
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
You can just replace them with a translatable component yeah - give the above linked docs a read they are very thourough
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?
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
You'd need to manually call GlobalTranslator#render(Component, Locale) for item names
Some typos found while reading the doc https://docs.advntr.dev/localization.html:
- Line 12 of the snippet here: https://docs.advntr.dev/localization.html#using-a-custom-translator
- // 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) {
- Second sentence of this paragraph: https://docs.advntr.dev/localization.html#using-a-translationstore
I think it is missingof:
It provides a simpler way OF creating a
Translator
But as english is not my native language, I'm not 100% sure about this
Feel free to submit a PR
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.
</!shadow>
Hi, should the rendering on https://webui.advntr.dev/ be 1:1 accurate to in-game chat?
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
i mean, chat box width is configurable so that isn't something you should be relying upon anyway
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
TIL, but imo it should match the vanilla defaults
got it; not going to open a PR cuz I'm lazy
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
Sounds good to me - did you have any thoughts about a good syntax?
can't think of one that would make sense off the top of my head
Well I was thinking before the reverse parameter !
But it conflicts with phase
I will move to #adventure-contrib
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
You need to attach a click event to your text - see Component#clickEvent(ClickEvent) and ClickEvent.openUrl
yes, but if the link is somewhere in a text I necessarily have to iterate all the text to find it and then apply the clickevent only to it, right?
there's an example in the legacy serializer impl
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? 😄
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
Placeholder.unparsed()
You would create your own MM instance which supports only the things you want to expose to end users
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)
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
Then deserialize out user input on one hand, and then deserialize the rest of your input with the user parsed stuff using Placeholder.component()
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
How the hell did you ended up in that situation? Please explain your XY
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
Basically, no
Without any control over the placeholder replacement made by the other plugin, you won't be able to safely do anything
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
No it won't change anything, because user will still be able to get out of that wrapping tag earlier if he knows the end sequence
Yea, true
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
honestly the best thing you can do is not do that
dont use your awful plib plugin, edit the existing one to use minimessage
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
}
christ
now all the user needs to write is </red>evil hover event <red> lmao
better idea yes
see MiniMessage#stripTags
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
check if input equals output and block if it doesnt
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
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
I was tempted to drop spigot support because of stuff like that lol
good idea :p
I was going to but decided against it for now, sorry
the effort to continue supporting is minimal so if that changes...
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?
the global instance is fixed, that will not change until we have namespaced tags
ooo namespaced tags
love that
so my best bet for right now is likely using MiniPlaceholders then, since this placeholder must be a component?
ya
dang, well thanks for the help!
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()'
Looks like you're mixing and matching versions you're relocating?
let me double check, but I don't think so because I control all versions from one spot.
I mean, if you have adventure code calling a method that doens't exist, what else would it be?
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
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
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?
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
i for one wish we didnt bother with all this anti-woke stuff and just stayed with homogeneous lists 😒
okaay, thanks for the responses
ok why did you guys remove like all non-basic methods from Index?
like keyOr(), valueOr(), getting the maps, etc
"remove"?
https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/Index.java#L184 seemss like those methods are there to me
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 💛
anyone know a way to parse an md5 BaseComponent to adventure?
im updating deprecated APIs so this is rather fun
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.
yea thats pretty much what i did rn
uhh that's json-to-json-to-json, you should be able to use the gson serializer directly lol
They're just helping confirm all the serializers work, abby! 😆
how would i go about doing that?
look at GsonComponentSerializer
Try it and see!
no time
jk nah ill leave it in a bad way cause people arent intended to use it anyway)
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?
Well translators are just interfaces you can implement and do whatever you want with so yeah you can run arbitrary code
And of course I still want to be able to translate stuff using MiniMessage
Alright yeah, just wasn't sure if this was the correct way to do this
Yeah so I'm not sure how this would work. I have the translation string <placement> place with placement being the ordinal. But of course the ordinal will be translated using ICU4J, so I need to pass it as a TranslatableComponent, right? And then translate when it hits the GlobalTranslator. Or am I having a brainfart here
can't you just make the placement tag insert the ordinal?
I'd prefer to only translate it when it's sent, not upon constructing
Why?
It's for a hologram, and I don't want to make a separate hologram for every player
Seems unnecessary
Right fair enough, then make a custom tag
you can see e.g. TagResolver.WithoutArguments for a simple lambda one
Wait, but in this case, the translation is not done client-side, but server-side, right?
Yes
Your hologram is a textdisplay?
yes
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
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
Since it works for text displays, I'd guess so, yes
Wouldn't this mean I need to serialize it to MiniMessage and then deserialize it?
Are you using a MiniMessageTranslator?
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())
)
You need to set the target using Argument.target
right... but I don't know the target at this point
hm right
I suppose we could make the target default to Locale if not set, and let the target argument overwrite it
that'd be nice
let me whip up a pr
thank you
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?
You're massively overcomplicating things here
Are you trying to support both legacy and MiniMessage?
yes!
Unfortunately we don't provide support for mixing formats like that
since when? I remember in older version this was working?
Mixing them has never been a good idea. You should one-time migrate outdated configs from legacy, and then going forward use minimessage.
I'm talking about using both formats (legacy and minimessage) together, we don't and have never provided support for that
so I need to change configs from legacy color codes to all mini messages like: <color:#5555ff>, right?
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
I mean, you translated it to a legacy ampersand string
So... you left code in that does nightmare conversions to legacy. 😆
I removed legacy conversions and changed everything to minimessages format
You definitely didn't - can you show us the updated code?
Component nameComponent = SupremeTags.getMiniMessage().deserialize(name);
meta.displayName(nameComponent);
MiniMessage miniMessage = MiniMessage.builder().build();
and that plain MiniMessage instance is what SupremeTags.getMiniMessage() returns?
Print name prior to going into deserialize. I bet it's already been poked at.
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:
'
message.txt by @oak hamlet: https://pastes.dev/LKVq2HXwrc
idk whats going here
all souts from here are string going to MiniMessages deserialization
where are you taking name from in here?
complete code always helps
from config file
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
Judging from this, it does not seem like withName causes the error, but rather your lore. Please share the code handling the lore instead
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;
}
applyPlaceholder looks sus
And SupremeTags.getMiniMessage()
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;
}
Then StringUtils.replaceEach, if that's part of your codebase and not a standard library like apache commons
atp i'd suggest stepping through your code with a debugger and working out where the legacy is getting added
this is from apache commons
also would suggest making sure that you are running the code that you are writing - possible you could be running an outdated jar
im using the newest one which I had build
is the error present in this same log? this suggests it's all working fine
message.txt by @oak hamlet: https://pastes.dev/GUX5C9w2Bh
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()
yep, that would be it
You must use meta.lore() instead to get the lore as components
yup
Do I need to write
actionText = actionText.append(Component.text("Hey"));
?
Yes
Components are immutable. You could instead use the builder, Component.text()
I am adding an item lore into one component
Component actionText = Component.empty();
for (Component l : itemLore.lines()) actionText = actionText.appendNewline().append(l);
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 🙂
just sharing what I am trying to do
Okay! Have fun 🙂
I'd consider looking into Component#join - in your example you could simply do Component.join(JoinConfiguration.newlines(), itemLore.lines())
Very good ! Thank you
Umm
the client does not render newlines everywhere
☹️
you can theoretically achieve multilines everywhere with a custom font in a resource pack if you really need them
right, but to do so I need to use ascent and negative spaces
yeah
Also is there a way to style a component like when using MiniMessage.miniMessage().deserialize()?
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~~
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)
I think my question is not relevant since I gave my self an item using the give command and just wrote "<red>hey"
so you should write {text: "hey", color: "red" } instead
Is that a title?
action bar
Ic
is implementing ForwardingAudience a bad idea?
class structure is: Game -> Teams -> Players
it's intended to be implemented
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
👍
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
because mojang changed the format
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?
does it work if you don't mix it with legacy formatting?
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.
Yes: use MiniMessage instead of legacy color codes
Well, main issue is that Minimessage obviously doesn't work in translation files.
So at least there would I need them
yes it does?
I mean without having to hijack the entire translation thing
"hijack"?
huh? that is built-in
it's part of adventure
unless you mean vanilla translation files in resource packs, those indeed only support § formatting
I talk about MC's translate thing
Like why would I talk about anything else when I mention translatables
....I do use a resource pack you know...
they aren't very powerful
Okay cool, glad we could help then 👍
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?
Since when was fallback a feature?
I think around 1.20?
It's 1.19.4 apparently
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?
.decorationIfAbsent(ITALIC, false)
That's covered by the faq
TL;DR: Most methods come with *IfAbsent alternatives, applying the style/color/decoration... only when not explicitly already present
omg there's the whole docs on docs.advntr.dev about it while I only looked at https://docs.papermc.io/paper/dev/component-api/introduction/
I feel stupid now
thanks
does the adventure api have conversion to and from the nms equivalent classes?
Adventure doesn't, because it is platform agnostic
Platforms will certainly implement that somewhere.
oh ok i'll ask in the other channel then, thanks
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
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
On 1.21.4 or 1.21.5?
1.21.5 sorry
using platform-bukkit?
not updated yet, PRs welcome
(there is a PR)
not for all of the 1.21.5 issues
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?
That's still not slow
I asked for unit and you said nanos
I doubt it's 0.0002 ns, but still the value is low. However, this test is irrelevant because that's not a proper Java benchmark. I suggest you to look into tools like JMH if you're interested into that kind of things
https://openjdk.org/projects/code-tools/jmh. Contribute to openjdk/jmh development by creating an account on GitHub.
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)
sounds like its not laggy at all
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!
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
A parallel stream probably hurts more than it helps for this
lots of questions that could be answered with profiling lol
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.
What's the xy? can point you towards a good solution then
Obfuscating the playername(s) in death messages if they are invis
so it'd be replacing the playername with "<obfuscated>--------"
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
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
this implementation basically does what I mentioned but with the plaintext serializer, which loses formatting
Just iterate through the arguments then, plain text each component, check if it's a player name and then obfuscates
Until your player "diamond_sword" wins a fight while invisible 😀
that's a funny case I didnt think of that honestly I can live with lol
but yeah that makes sense I kinda forgot that you prolly can just iterate over all components in the message
it's the arguments you'd want to iterate over, death messages are just one translatable component
oh so arguments are a separate thing from the children of the death message component?
A translatable component is a key and arguments. In this picture, the key will be something like death.kill.with_weapon (or w/e) which in english would be %s was slain by %s using %s
The arguments of the translatable component are inserted into the resulting string in the %s spots
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
Yes, Component is an interface implemented by all component types, e.g. TextComponent, TranslatableComponent, etc
so am I right in assuming that I just cast that specific Component to TranslateableComponent?
Technically, plugins can modify that component in the death event, so unless you’re 100% sure about your plugins, you should check an instanceof before the cast
fair point
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?
you're discarding the modified component
check your ide warnings, components are immutable
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
heck yea
as? is your friend as well, you could
val component = e.deathMessage() as? TranslatableComponent ?: run { error; return; }
you could just do val newComponent = component.arguments(newArgs) lol
oh that returns a copy
ohh
its the "for this component" that tripped me up making me think it modifies the instance, but yeah returns a copy ig
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
Thanks for the help guys :D
with all the time you're wasting writing messages on discord, you could have gathered data that could lead to improvements
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
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.
What platform are you using?
and just to make sure, do you have the ambient slider turned up?
yes everything is maxed out, i don't think game settings is the problem
i tried all source type too
_ and not .?
item.flintandsteel.use is how mojang refers to that sound, albiet 1.21.5
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
I didn't try the . ones. will do it now
That was the case. Thanks all!
is ~22mb many for just each sign ?
I saw geyser uses Adventure Renderer for Signs to translate content
What kinds of signs do you have that use up 22mb 👀
Jesus
Idk if that one or many but from my opinion is already to much
Yeah that is way way way too much
I fixed already 28mb memory allocation for just a replaceAll call
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
Sign allocated the most one
Again, I do not know where the numbers are coming from. You might probably want to provide a bit more information
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
It is an immutable API, there will be lots of allocations
yeah but not 55 MB of components lol
I do it tomorrow. Or is a hprof enough ?
Yeah I can take a look at a hprof tomorrow

@cloud vapor Here we go took me a littlebit longer.
Here is the entry point
tbh im not really sure what your issues are here, nothing here looks problematic
The style allocates many memory from my opinion
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
Is there some mutable form of audiences
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
is there no audience.remove or audience.add
Audience isn't a collection?
its a grouping of a viewers
It's an interface
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")
What are you trying to do and on what platform?
i just needed something like this
class MyAudience : ForwardingAudience, HashSet<Audience>() {
override fun audiences(): MutableIterable<Audience> {
return this
}
}
you can use Audience#filterAudience()
that's not at all mutable audience
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?
ngl i have no idea what you're tryna say
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?
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.
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?
They are separate modules, but all of them (or at least, all you should need) is already bundled with Paper yes
Amazing, then I'll get started on the docs. Seems pretty straightforward, looks better than my hacky ChatUtil class lol
Thanks!
NBT isn't bundled
Does this &6 also count as legacy? Or only the § prefixes?
Both are legacy
Yes they are both legacy and shouldn't be used anymore
Well, that's why I said "all you should need" x)
I'd argue NBT can be useful often
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.
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.
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
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
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
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
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.
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
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
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?
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 💪
Our examples would be the gradient family of tags, but they are way too complicated for this. You'd ignore the other two methods and only implement apply - it gives you the component you're visiting and then you just return the modified component. So you can simply just do return component.color(darken(component.color())) with whatever darken logic you may want
Does it give me them recursively or do I need to do it myself?
To deeply apply the darkening
It gives you them recursively iirc
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.
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
There is also no universe in which the code examples you provided are "simplifying tag handlers".
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?
That’s handy. Do you think such tag would make sense in the standard tags? Specifically darken and lighten.
If you want, I could try to do them cleanly and open a PR with them
Yeah, I think that'd be a very neat PR!
You have given examples of functionality you need. You have given zero examples of real-world problems that functionality would solve.
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?
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
Ok, I'll try to find some time soon then 🙂
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
I don't see what this has to do with the api - parsing colour codes works fine for me
This is what happens
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
definitely can't reproduce that (https://webui.advntr.dev/?x=kMJRTzK1Y6), are you sure that code is what you're actually running? can you paste the output that is being sent to the client?
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
lead with the fact your papi tag is inserting the colours next time :p
Yee forgot to mention, sorry
the papi tag we provide an example for in the docs uses legacy formatting to produce the components, not minimessage
Oh, so I have to write my?
you'd need to make your papi tag use minimessage instead of the legacy serializer
So changing this line
final Component componentPlaceholder = LegacyComponentSerializer.legacySection().deserialize(parsedPlaceholder);
Would do the trick?
yes
Like that I assume?
final Component test = MiniMessage.miniMessage().deserialize(parsedPlaceholder);
yes
Thanks a lot! :D
🫡
Are you sure it works as you intend? 👀
I think you rather need your papi tag to insert the raw string with Tag.parsed(...)
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
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 😵💫
its preProcessParsed
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 𞉀 to [#123456](/guild/289587909051416579/channel/123456/)) are probably not applied recursively to the content of the papi values
Oh yeah I mixed up Tag and Placeholder, my bad
So I need to run the code as often until everything is resolved?
no that's not how it works unfortunately
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
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..`
OR, surely Kezz know better, but I think there's a notion of text pre-processing and component post-processing in MM
So I need to preprocess the whole message until everything is "parsed", and then with a helper method I'll send out the message?
The best approach to translate legacy is to open Notepad++ and https://webui.advntr.dev
And do the migration once for all
Ye, I'll just put minimessage in the default config
Legacy is still so widely spread among other plugins, it's crazy
Gotta start becoming a part of the solution not the problem lol
heck yea that's the attitude to have 💪
you'll save yourself a lot of hassle that's for sure
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
you're very welcome to!
Is there also a clean way to send to console in the adventure api?
Still using Spigot api 😭
adventure itself does not have a console
then that question would be more related to adventure-platform, not the api
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
what do you mean by adding points to translatable component methods? you can already set the target of a translation (assuming you're talking about minimessage translation stuff) with Argument.target, is that not what you'd want?
maybe you could explain what youre trying to achieve and then we can point you in the right direction
On mm.deserialize you can pass a pointer, eg player that can be used in tag resolvers on an mm instance, if you want to use the built in mm translation stuff you cant pass a pointer to the translatable to be used
yeah you can, Argument.target
happy to help! 🫡
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?
viaversion translates components to the legacy equivalent
hex colors will be replaced with the closest legacy color
Oh awesome, thanks
And things like shadow will probably not even work on previous versions, right?
havent tried it but i bet it just omits the shadow
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?
You may be interested in the Formatters: https://docs.advntr.dev/minimessage/dynamic-replacements.html#insert-a-choice
I'm using a third-party plugin which uses MiniMessage formatting, so I don't think they apply to my case unfortunately
MiniMessage does not have any in-built tags like that no
How would your condition be evaluated then?
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
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
it's next up on my big minimessage todo list
I must say it's kind of a long awaited feature on my side 🙂
still need to decide what the best separator is for namespace vs name
what does seperator mean in this case?
I feel that the : for separating name and args is not the best
It would better fit the namespaced name like everywhere else
annoyingly we can't do : bc it separates arguments
im probably leaning towards namespace/name
And having idk maybe whitespaces for separating name and arguments
And then allowing named arguments
yeah named arguments are v easy to fit in our "spec"
Well, there's a little bit much to think about, but I deeply think that kind of change IS the opportunity to do so
just name argname=value
how do you (plan to) disambiguate
namespace=""
name="sub/path"
and
namespace="sub"
name="path"
?
or is it not for vanilla keys / has a different allowed path character set
We're only talking about the MM tag resolver names
ah ok
I doubt / is an actual allowed character in such place rn
yeah sub/path isn't a valid tag name
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
well do you register anything wjth the translator?
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
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
I never used the adventure translation system but this looks like you're using the wrong method?
i didnt read the docs
using TranslatableComponent instead of translation key works
but thats weird sht ngl
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
this method is called only if translate(TranslatableComponent, Locale) returns null.
thats weird
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
this is correct yes
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
no
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
nope, they're just all strings
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
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?
<lang:hello:arg1:arg2…>?
yes with minimessage i know i meant i cant add arguments to already translated components
described that a bit weird
if you use Component.translatable with the string key you can provide arguments on that
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
Did you add your translation store to the global translator?
^^
yes
arguments aren't %s, read the docs https://docs.advntr.dev/minimessage/translator.html#using-a-minimessage-translator
i mean Component.translatable("hello", Component.text("NonSwag"))
on translate
yes, read the docs :p
oh okay thanks
but still using minimessage only yields the tag itself
is that platform specific?
whats your updated code now
yeah it'll be good to see the context in which you're sending the component
what platform?
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?
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
lead with that :p
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
thats not right xD?
i am on 4.20.0
latest release
ah appears that was added changed after that release
yeah we haven't released 4.21.0 yet
seriously i have no idea what i am doing wrong
i read every page of the docs multiple times
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
i just want to confirm if that even works thats all
i am passing arguments into the lang:... 😭
to me it looks like you only have lang:hello not <lang:hello:argument>
last image
stuff passed into a tag will be unnamed, use arg:0 iirc for those
is there something i can do to make the docs clearer? i thought I'd explained it all pretty well in there :/
id say its pretty clear to me
i thought that is just for use with Component#translatable and not the mm tag
the lang mm tag is a translatable component
all your code is doing there is a roundabout way of creating a translatable component
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
well next time make sure to read them clearly :p otherwise we're just basically reading them for you
i did, i just dont get them
doesnt have to be because they arent written well
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
What do you mean to detect?
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
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?
exactly
There's no way to be certain.
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
I feel like you're dealing with a bad design issue. You shouldn't be getting random text from other sources like that. 😆
aint that the truth. just my eagerness to accommodate everyone
Accept only minimessage and move on. 
in this case I'm purely stripping the color/format codes because the text is all I care about
is there any guarantee that Component.empty() always equals MiniMessage.miniMessage().serialize("")?
should be yeah
okay so apparently you do not want to return "" in MiniMessageTranslator#getMiniMessageString.......
it took me a while to figure that out
that is a funny one
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?
what string are you providing
what platform are you on?
I figured it out, forgot a character
Is there a way to center a component in chat?
I mean yeah, but there is a method for regular strings, but it doesnt work for components
What method are you talking about?
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
are you making a custom plugin?
and do you mean translations?
kinda I'm updating an old plugin
i mean how do i send a game info message, like those bed messages, i dont even know if they are still explicitly different
well minecraft has lang files for their messages if you want localized messages
aka, translated to the clien't locale
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
the bed message is an action bar message?
at least 3 years ago they were different, so that's why I'm asking if they still are or aren't
not that I know of, but atm Player implements Audience which you should use
ye ye i know that much
also just tested, they are different, they are a system chat message with the overlay parameter set to true
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
please respond in #adventure-platform-mod-help
i just realized this is the wrong channel
just tag me
There are ways to center a message, but it's not intended for components
That code won't work if the client resized its chat
Are there any ways to send a player a centered component?
I am aware, but I am still wondering how I can center a component
You can apply the same hazardous logic
Well i tried, and its not centered
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
I see, thanks for clarifying
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
We need <pink> as color in the next update 😭 Hat to always search a hex
Isn't <light_purple> pink?
ohh, thats the way they do it
Hi there, is that normal the lang tag does not seem to work with item stacks, when using a custom MiniMessage translator?
For slightly more detail, these are the original 16 colors notch defined. 🙂
using messages for instance it works fine:
ahh, okay
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
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
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 ...
Not without a resource pack I dont think, but I'm not 100% sure
imma do some researches, thanks !
Btw what platform is this on?
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.
Oh oki, as it was directly related to MiniMessage's trnaslation system I though this was the right channel sorry 😅
Nah it's fine
I'll make my own system anyway, from some resaerches on neofgre's forums it does not seems possible 😦
thanks for the help tho 
The big issue with sending different items internally iirc is creative mode, since they copy the item as-is
Correct, on Paper the GlobalTranslator is not used for items
So if packet tricks are used to translate it, they would copy it with the translated, not the translatable name/lore
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
what if I let every item be built based on player's local, then if dropped then picked up, it scans its ID to build it again with the new player's locale?
my main goal was using that translation system with GUI, which I would have done myself before knowing global translators wouldn't work xD
just a random thought, wouldnt it be possible to always add the translation as a fallback so items CAN be translated but are just what locale is defined as fallback?
If you set the name yourself, storing the language + type in pdc is a good idea, then you can override visible stuff based on that as needed.
I mean yeah vanilla has translation fallback in the components, so you could have a single language instead of the key
But the global translator would still ignore it
Oh sorry I see what you mean
Added the translated string as a fallback would still modify the item
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
thats what i do for translations so the player can see the english version without the resource pack
creative wouldnt brick the items though
So if 2 players with the same item both put it in the same chest, they would not stack
oh no it would nvm
ah yea didn't think about that
You could avoid bricking the item with an internal Uuid refering to the "virtual" stack that gets converted back serverside.
But that is very internals invasive
just disable creative mode
easy fix
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
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?
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
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:
Is there any reason why you're making your own lang component?
MiniMessage has a pretty powerful translation system built in https://docs.advntr.dev/minimessage/translator.html
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
No reason to make your own system though, you can manually translate components via the GlobalTranslator
ah?
you mean I keep the tw-lang tag, but i'm using the global translator to handle format and args?
GlobalTranslator.render(Component, Locale)
Ohh hum, what would the component be?
No, just use the global translator to render your components and use the existing MiniMessage translation system
the key?
Component.translatable("my.translation.key") or w/e
yea but how would that work for items?
give the docs a full readover for details on the system:
e.g. I just use <lang:my.custom.key>?
No, you'd set the name/lore and then translate them manually using the global translator
then sets the name to the translated component from the translator?
Yes
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
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 😅
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
so it does apply to MM? I'm only using MM, not creating componennts like your example
MiniMessage creates components
but doing <accent>→ <shade-red:500><lang:key> for instance does not work for items as I've tested
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
ahh got it
First I deserialize the raw text into component, then pass that componennt into the translator
so it parses it manually anyway
I highly encourage you to give the docs I linked a thorough readover, they are very comprehensive
happy to help!
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?
Lore doesn't support newlines, so you should be using a list of components
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
is message.join actually in the bundle?
Sorry, I meant it's not translated into another language except US
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
Check the docs I linked you earlier, they are very comprehensive
Nothing wrong with having multiple translation stores tho
Yes, it's working now. Just assumed my registration ain't a problem without thinking about it
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
did you create a custom minimessage instance? if yes you need to add the tags
aren't they added by default?
and other tags seems to be working anyway
I end up with for instance
add StandardTags.defaults() to your builder on #tags()
Are you using that as the text for a translatable component?
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
Yeah, that's working as intended, translatable content doesn't contain formatting, handle that in the wrapping MM string
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?
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);
}
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()
why does adventure uses an old version of gson?
https://github.com/KyoriPowered/adventure/blob/main/4/gradle/libs.versions.toml#L42
what in the backwards compability is this? 💀
Yes there is a point, it breaks plugins that try to use newer versions of gson
Even with adding the library to plugin.yml and shading the gson library, the error persists
how
the minecraft server has a certain version of gson, adventure doesn't change that
no idea what voidgen is
the classloader hierarchy just doesn't work like that
ItemEdit downloads adventure as via plugin.yml
and?
Which loads gson 2.8.0
no
Look at the logs
why? the logs don't say anything useful
Voidgen (my fork of the plugin) loads gson 2.13.0
well, I fixed it 🙄 I just relocated gson
yeah but you didnt override it
Whats the point of JSONComponentSerializer? Gson wasnt sufficient?
Allows you to not care about what backend serializer you're using
is there a serializer that makes it possible to send components with com.mojang.brigadier.Message#getString?
platform independent preferably
I mean, we have adventure-platform yeah
i cant find anything related to Message
i want to create a command syntax exception with a component as message
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())
)
);
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
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...
Instants don't contain information needed to produce a date like that
so is there a serializer for doing that or not?
Well then the parameter type is slightly missleading, as it accepts any TemporalAccessor...
I'm honestly not sure what you need here, I'm pretty sure they don't accept components anywhere in raw brig
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
There are other TemporalAccessor implementations that it does accept, e.g. ZonedDateTime, LocalDateTime, LocalTime, etc etc
The only "common" interface between them is TemporalAccessor
i could swear that was possible
but looking at how paper converts the components it seems like that doesnt work anymore
if I convert a component to plain text and it includes a translatable component, will that text be the translation key?
depends on the flattener that you use
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?
The default depends on the platform you're using
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
wdym show item in chat?
as a hover event? because thats built in
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
I never played with a mod like that
I was only wishing I know it's not possible in paper etc. My issue is the translation question
Paper will translate everything in the PlainTextSerializer yes
dunno about adventure-platform
platform-mod will, I think the legacy platforms might if you're explicit abt the flattener
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
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
is minimessage in paper too?
yes
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
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
anyone have thoughts about how to even start attempting to fix https://github.com/KyoriPowered/adventure/issues/1157 adventure is so big idk where to actually start
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
after a quick look i think that first part wouldve probably been my thought about it too
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
yeah it could def go down a really long rabbit hole on that
yeah 😬
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
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
you mean test in game?
probably includeBuild adventure in Paper or adventure-platform-mod
test anywhere tbh, just to see what the renderer is actually creating
yeah I'd just run a test, we should have tests for the renderer somewhere
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
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() ?
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
but that value is generated everytime the player execute the command, so should i just use replaceText then ?
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
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
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
That's interesting. For sure there isn't, but that's a good coding exercise to familiarize yourself with MM tag resolvers imo
so is it possible to create one?
Definitely yes, using a Modifying tag
I'll take a look, thanks
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
it should be Tag.modifying? not find it here
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
}
}
