isuckatcode-pages/Minecraft Datapacking/When Two Macros are Faster than One.md
themodernhakr 7d6c3784a3
All checks were successful
Update pages on webserver / Update (push) Successful in 6s
vault backup: 2025-03-24 23:50:46
2025-03-24 23:50:46 -05:00

4.6 KiB

title draft
when two macros are faster than one false

While working on my Database datapack (still WIP), I knew I'd want to find

scenario

dataset

The data is stored in a storage #_macro.array. Array is populated with a total of 500 entries, each having id and string fields.

[
	{
		id: 1,
		string: "entry1"
	},
	...
	{
		id: 500,
		string: "entry500"
	}
]

constraints

The objective is to create an interface that receives a keyword, say entry500, and searches #_macro.array for an entry where the value of string matches the keyword.

The keyword must be able to be entered by a player at runtime, and #_macro.array can have an arbitrary number of custom entries created by a player.

In TypeScript, it would look something like this:

function searchArray(keyword: string) {
	// logic
	return theRelevantEntry
}

searchArray('entry500')

In mcfunction, this is not so straightforward. Macros would make this really clean:

function test_namespace:search_array {keyword: "entry500"}

Unfortunately, macros come with a performance hit. A more performant method, albeit less elegant, is to store the keyword in NBT storage prior to calling the function. The storage can be removed after the function is run:

data modify storage test_namespace:test_namespace temp.keyword set value 'entry500'
function test_namespace:search_array
data remove storage test_namespace:test_namespace temp.keyword

Once the entry is found, it is stored in the temp.result storage, which can then be consumed by another function.

Now for the logic to do the actual array searching...

one macro

Macros allow us to reach into our array and pick out an entry that matching value in the string property. This is something that I didn't realize (for some reason) and was pointed out by PuckiSilver and amandin on the Datapack Hub discord server.

... one_macro.array[string:$(keyword)]

This method is super clean and results in a one liner that is wordy but simple:

'# one_macro/_searcharray.mcfunction

$data modify storage test_namespace:test_namespace temp.result set from storage test_namespace:test_namespace one_macro.array[string:$(keyword)]

_searcharray can then be called using the temp.keyword storage:

'# one_macro/run.mcfunction

data modify storage test_namespace:test_namespace temp.keyword set value 'entry500'

function test_namespace:one_macro/_searcharray with storage test_namespace:test_namespace temp

data remove storage test_namespace:test_namespace temp.keyword

'# call the function that consumes 'temp.result', then remove it
data remove storage test_namespace:test_namespace temp.result

two macro

Another way to crack the problem is through indexing. This was my original plan when I didn't realize that ...[string:$(keyword)] was possible.

This method requires the creation of an index of the field that is going to be searched. The index will look something like this:

[
	{entry1: 0},
	{entry2: 1},
	...
	{entry500: 499}
]

The key, e.g. entry2: corresponds with the value of a string field in the main array, while the value 1 indicates the main array index where we'll find the full entry. The index can be searched with a direct path, index.$(keyword), and the main array can also be searched with a direct reference to the entry index, array.#(index). Keep in mind that the index must already exist prior to running the search function. In a practical application, an index could be updated every time the main array is updated. A scheduled task could also audit the index to ensure that it's up to date.

The index search looks like this:

'# two_macro/_searchindex.mcfunction

$data modify storage test_namespace:test_namespace temp.index set from storage test_namespace:test_namespace two_macro.index.$(keyword)

And the array search looks like this:

'# two_macro/_searcharray.mcfunction

$data modify storage test_namespace:test_namespace temp.result set from storage test_namespace:test_namespace two_macro.array[$(index)]

The index and array search functions are then called using the temp.keyword storage:

'# two_macro/run.mcfunction

data modify storage test_namespace:test_namespace temp.keyword set value 'entry500'

function test_namespace:two_macro/_searchindex with storage test_namespace:test_namespace temp

function test_namespace:two_macro/_searcharray with storage test_namespace:test_namespace temp

data remove storage test_namespace:test_namespace temp.keyword
data remove storage test_namespace:test_namespace temp.index

'# call the function that consumes 'temp.result', then remove it
data remove storage test_namespace:test_namespace temp.result