3

The two questions (around the same principle):

Is it possible to index the newly introduced in 1.14 NBT and Entity raw JSON command (e.g. /tellraw @s {"nbt":"Attributes[0].Base","entity":"@e[type=horse,sort=nearest]"}) so that when it returns the results for multiple entities you can format the results?

If the above isn't possible, is it at least possible to bridge the gap between returning all an entity's data (Attributes[].Base) and only one part of the entity's data (Attributes[0].Base)? i.e. Attributes[0,2,7].Base - this doesn't work, but is there something like it?


Details:

So since a lot of the old horse attribute mods haven't been updated to 1.14, I decided to give building an in game version via commands a spin. Luckily, 1.14 added a really cool feature that allows you to effectively scan the NBT data of a bunch of local entities with the /tellraw command and raw JSON text (thanks to Fabian Röling for providing the details here and here).

An example of how you can use this command to scan horses is by using this command:

/tellraw @s {"nbt":"Attributes[0].Base","entity":"@e[type=horse,sort=nearest]"}

Which returns a list of the health stats of all individual horses, from nearest to farthest:

19.0d, 23.0d, 20.0d, 22.0d, 20.0d, 20.0d, 19.0d, 22.0d, 22.0d, 21.0d, 23.0d, 27.0d, 21.0d, 17.0d, 17.0d, 19.0d

This is great, except there are two more stats we care about with horses, speed and jump height. We can add speed to this line along with some flavor text with the command:

/tellraw @s [{"nbt":"Attributes[0].Base","entity":"@e[type=horse,sort=nearest]"}, " Health ",{"nbt":"Attributes[2].Base 100","entity":"@e[type=horse,sort=nearest]"}, " Speed "]

But the results are less than stellar:

19.0d, 23.0d, 20.0d, 22.0d, 20.0d, 20.0d, 19.0d, 22.0d, 22.0d, 21.0d, 23.0d, 27.0d, 21.0d, 17.0d, 17.0d, 19.0d Health 0.27321843060292755d, 0.21480220332502717d, 0.17749702015266722d, 0.28170043791890875d, 0.24102104888670942d, 0.20762532646388024d, 0.1808260524268614d, 0.2572008833733531d, 0.19745627778622357d, 0.19073958560813306d, 0.21073516590775235d, 0.21222213167329812d, 0.16440016044525307d, 0.2815179975462115d, 0.19912091795196668d, 0.1991595317469814d Speed 

It lists all the horses' health followed by all the horses' speed. This makes sense, but ideally we'd want the result to give us the first horse's health and speed, followed by the second horse's health and speed, and so on, to make it easy to compare the horses.

It should be noted the both lists return the horses in the same order; the horse with 19.0 health is also the one with 0.273218 speed. So if you were able to actually index and format the arrays (or lists, or really strings), you could easily put the correct data together. However, it appears the NBT and Entity commands return their output data as a string, and I haven't been able to figure out how to format it for the life of me (much less turn those doubles into ints).

Is there a way to index and format the output from the NBT and Entity commands like an array?


The second part of this question comes as an off-branch of the first. Ideally it would be best if one could format the output, because then we could make it look pretty and exactly as we liked. However, there is another way of retrieving the NBT data that keeps the individual horse data together. You can just use the Attributes[0].Base, but not put a number in the index: Attributes[].Base. The issue with this is that it returns all the NBT Base data about a horse, which leads to a mess. We're really only interested in the Health (0), Speed (2), and Jump Height (7) of the horse. I gave a stab at trying Attributes[0,2,7].Base, but obviously this didn't work, but it made me consider other methods. I saw there was a way you could also put specific NBT compounds into the index, but I couldn't find good documentation or examples on this.

Is there a way to reference multiple attributes in a single attributes index, i.e. like Attributes[0,2,7].Base, but that works?


Edit: Goal

added this in since I figured I should include what my ideal goal is. This is how I'd like the text to look in the best case:

19 Health 273 Speed 62 Jump
23 Health 214 Speed 73 Jump
20 Health 177 Speed 53 Jump
....

That's what I'm trying to get the text to look like, with each line being a different horse. This is what the first question is about and what I mean when I say formatting. The second question assumes the above is not possible, and tries to work out a compromise.

Another note, doing this with function files, not command blocks, though I imagine it shouldn't matter.

  • 2
    About formatting: Can't you just use "bold" and the other JSON tags like normal? About sorting: This is indeed a good question, but it's a bit buried in this long post. Is that your main question? I think the only way would be to copy the data over to dummy entities that you sort however you want, then using sort=nearest. About multiple indices (your 0.2.7 example): Sure, just use multiple JSON compounds in a row, with commas between them. Also, I wouldn't recommend using the index for selecting the attribute, but rather the attribute name using the [{…}] syntax. And no, Minecraft … – Fabian Röling Jul 29 at 0:50
  • 2
    … does not allow any string editing beyond exact compare and copy. About grouping multiple different values together, maybe you could copy them to some arbitrary custom tag on dummy items and then print their NBT. That would even put brackets around your groups. The short answer to your question in the last line is "no", but I think I can still help you. I'll write a proper answer, but first I'll sleep. – Fabian Röling Jul 29 at 0:53
  • Thanks for the ideas @Fabian, and sorry for the length. I should have spend more time simplifying my post, but it's admittedly been a head scratcher for me as this is my first time diving into Minecraft commands, so I didn't want to leave an important detail out. Looking forward to your answer. Also, to answer your question about formatting, can definitely bold the text and change colors and the like. I was thinking of formatting in terms of C-style string formatting, and convert the doubles returned by the NBT command to integers while also scaling them. Unlike /data get, I can't use scale. – Scott F Jul 29 at 5:52
2

I assume this entire question comes down to "how to print NBT tags A and B of multiple entities like {A1,B1},{A2,B2},{A3,B3} instead of A1,A2,A3,B1,B2,B3". The best way I know to do that is to copy the data over somewhere else in a way that already groups it. Luckily, items allow any data in their tag tag. So you could for example create an array of compounds in an item entity.

First, summon it like this (it's marked with the "dummy" tag, item type and count don't matter, but must be present):

/summon item ~ ~ ~ {Tags:["dummy"],Item:{id:"stone",Count:1,tag:{HorseAttributes:[]}}}

Then iterate over the horses and create new entries in the array, then fill them (this is also where you can apply sorting, for example by distance). For each horse, do this (until you're out of tagged horses, you can for example do this in a recursive function):

/execute as @e[type=horse,tag=!copied,limit=1] run data modify entity @e[type=item,tag=dummy,limit=1] Item.tag.HorseAttributes append value {Health:0d,Speed:0d,Jump:0d}
/execute as @e[type=horse,tag=!copied,limit=1] store result entity @e[type=item,tag=dummy,limit=1] Item.tag.HorseAttributes[{Health:0.0d}].Speed double 0.0000005 run data get entity @s Attributes[{Name:"generic.movementSpeed"}].Base 2000000
/execute as @e[type=horse,tag=!copied,limit=1] store result entity @e[type=item,tag=dummy,limit=1] Item.tag.HorseAttributes[{Health:0.0d}].Jump double 0.000000001 run data get entity @s Attributes[{Name:"horse.jumpStrength"}].Base 1000000000
/execute as @e[type=horse,tag=!copied,limit=1] store result entity @e[type=item,tag=dummy,limit=1] Item.tag.HorseAttributes[{Health:0.0d}].Health double 0.0000005 run data get entity @s Attributes[{Name:"generic.maxHealth"}].Base 2000000
/tag @e[type=horse,tag=!copied,limit=1] add copied

The reason for first scaling up and then scaling down again the values when copying them is that for some reason they only get handled as integers. But if you multiply them with a high value first and later divide them by the same number, you get more precision (jumpStrength for example rounds down to 0 almost every time otherwise). The numbers are chosen to give as much accuracy as possible without ever overflowing (different attributes have different maximum values, that's why they differ).
The Health tag is set last, because it is used to identify the not-yet-fully-written tag. I don't think there is any other way to identify a specific entry in an NBT list to only fill one of all that fulfil a criteria without knowing its index. Health 0 can't happen normally, because the horse would be dead. If you also want to index currently dying horses for some reason (if that's even possible), you can use Health:-1d or something like that as a dummy entry.

Now, finally, the output:

/tellraw @s {"entity":"@e[type=item,tag=dummy]","nbt":"Item.tag.HorseAttributes"}

The output can for example look like this:

[{Speed:0.2102355d,Health:21.0d,Jump:0.682921423d},{Speed:0.28055749999999996d,Health:24.0d,Jump:0.819681399d},{Speed:0.2159845d,Health:20.0d,Jump:0.653335773d},{Speed:0.2855935d,Health:25.0d,Jump:0.681110673d},{Speed:0.1827385d,Health:17.0d,Jump:0.750580213d}]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.