4

I'm trying to make a dynamical sized integer array in Minecraft 1.14.4. I've already tried different ways, but the problem was with all of those solutions, that I had to summon one entity for each index, and then having two scoreboard, which were holding the integer for every entity and the index for every entity.

Then I had to run something like this:

/execute as @e[type=minecraft:armor_stand,tag=array] if score #index INDEX = @s INDEX store result score #value VALUES run scoreboard players get @s VALUES

where #index is the index I want to read from, and #value is the return value. The problem with this is, that if you have around 1000 indexes, the game gets really laggy due to all the entities.

Has someone got an idea of how to do this in a better way? Maybe with the ability of 1.14 nbt arrays, but without the need of iterating through every index (because every command is taking some time to get executed, and I necessarily need the speed)?

PS.: I'm already using a datapack, so it won't be a problem, if the solution depends on functions.

3
  • So you want to set an array's length based on a scoreboard value? But what would you fill that array with? It's probably easier to have the length be automatically set when setting the values. Commented Feb 20, 2020 at 20:48
  • not just having the lenght set from a scoreboard, but also read from a variable position in the array, set in a scoreboard Commented Feb 21, 2020 at 15:23
  • When I first read this question, I thought "why would you even need this?", but by now I've referenced my answer to it in at least three different places, in completely different contexts. Commented May 4, 2020 at 22:23

1 Answer 1

3

Creating a dynamically sized array in NBT is somewhat easy: Just use a recursive function to loop over a score (which determines the length) and use /data modify […] append every time. Here is an example:

Setup:

/scoreboard objectives add array dummy
/scoreboard players set $value array 1
/scoreboard players set $length array 10
/scoreboard players set $const2 array 2
/data merge storage fabian:array {array:[]}

I'm using multiplication by 2 on every iteration to show that you can do arbitrary calculations with the values.

Function "fabian:fill_array":

execute store result storage fabian:array value int 1 run scoreboard players get $value array
data modify storage fabian:array array append from storage fabian:array value
scoreboard players operation $value array *= $const2 array
scoreboard players remove $length array 1
execute if score $length array matches 0.. run function fabian:fill_array

Explanation:
The first command copies the scoreboard value of the dummy player "$value" to the "value" NBT tag in storage.
The second command appends the current "value" tag onto the array. There is no way to directly append a scoreboard value to an NBT array.
The rest of the function just does an arbitrary calculation on "$value", ticks down "$length" and loops back to the start as long as "$length" has not reached 0 yet.

Note that this function will still do its calculation once and append that onto the array, even if "$length" is already at 0 or below. You should consider that in whatever system you are using this.


Now the more complicated part: Reading from an arbitrary index. There are two main ways to do this.

The tedious method: Have a big function with every possibility that might occur in your setup:

execute if score $index array matches 0 run data get storage fabian:array array[0]
execute if score $index array matches 1 run data get storage fabian:array array[1]
execute if score $index array matches 2 run data get storage fabian:array array[2]
execute if score $index array matches 3 run data get storage fabian:array array[3]
…

Of course this would be infeasible for very long arrays.

The copy+remove method: Something similar was suggested here (archive), but my method does not require writing the index into the NBT array, so you can use it on already existing data (created e.g. by the game or some other datapack).

First, copy the entire array somewhere else:

/data modify storage fabian:array copy set from storage fabian:array array

Then keep removing the first entry of the array as many times as you want (indicated for example by a scoreboard):

/data remove storage fabian:array copy[0]

Once your loop is over, simply read the value from the first entry that remains:

/data get storage fabian:array copy[0]

You can also do this backwards, for example if you want to read the n-th last entry in an array, run /data remove […] copy[-1] n-1 times and then /data get […] copy[-1].

This can also be used for searching. Tick up an index scoreboard every time you remove the first entry and compare the value to a scoreboard like this:

/execute store result storage fabian:array temp int 1 run scoreboard players get […]
/execute store success score $changed array run data modify storage fabian:array copy[0] set from storage fabian:array temp

The fake player "$changed" will now have a 1 in the array scoreboard if the current array value is different than the score you're comparing it to and 0 if it is the same (so you found the value that you were looking for).
The ticked up scoreboard then indicates the index. Alternatively, you can get the length of the remaining array like this:

/execute store result score $length array if data storage fabian:array array[]

And then you can subtract that from the original length.

4
  • first: thanks for the answer, that might be a good way of how to do it. second: is there any better way execept for looping thru the whole array and read directly from a specific position? Commented Feb 29, 2020 at 0:35
  • (I deleted my comment from 2020-02-29 00:56, this is a copy with the link replaced.) Nope, there is no other way, except for the "tedious method". BTW, I should have linked this anyway: I livestreamed answering this and another gamingSE question. Here is the stream archive, you can see my (hopefully) complete thought process that went into this there: youtube.com/watch?v=Y2Dev-Kzo-c Commented Apr 15, 2020 at 20:31
  • 1
    The backwards method is actually preferred even if you're indexing from the front of the list. This is because when you pop off the front, the system needs to shift the rest of the elements frontwards. When you pop off the back, that doesn't need to happen. When working with a front-based index, it's more efficient to calculate the reverse index using the length of the list and use the backwards method instead.
    – One 2 Many
    Commented Feb 16, 2021 at 19:02
  • Seems reasonable. I once did something like that in a Snake game on a calculator because it became unbearably slow over time with the forwards method. Commented Feb 16, 2021 at 21:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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