
Datapack not working on 1.21.4 with models using strings
surfrock66 opened this issue ยท 8 comments
Following the upgrade to 1.21.4, after restructuring our resource pack with the new custom model format, we found that the Custom Roleplay Data Datapack no longer worked. Below is console output of a single player vanilla 1.21.4 world, showing the following:
- I listed the datapacks, showing only Custom Roleplay Data v1.5 (downloaded from curseforge).
- I gave myself 1 carved pumpkin
- I examined the data of the object. The object is in slot 1 of the hotbar.
- I ran "/trigger CustomModelData set 2"
- The model did not update
- I examined the data for this item and the output is in the console
- I then gave myself a pumpkin with the custom model data embedded in the give command:
/give @s carved_pumpkin[custom_model_data={strings:["2"]}] 1
(into my 2nd hotbar slot) - The item in my hand, in an item frame, floating, and on my head all updated (confirming the resource pack works)
- I examined the data for the second item in the console output below
[13:07:40] [Render thread/INFO]: [System] [CHAT] There are 2 data pack(s) enabled: [vanilla (built-in)], [file/custom_roleplay_data_v1.5.zip (world)]
[13:07:40] [Render thread/INFO]: [System] [CHAT] There are no more data packs available
[13:07:44] [Server thread/INFO]: [surfrock66: Gave 1 [Carved Pumpkin] to surfrock66]
[13:07:44] [Render thread/INFO]: [System] [CHAT] Gave 1 [Carved Pumpkin] to surfrock66
[13:07:50] [Render thread/INFO]: [System] [CHAT] surfrock66 has the following entity data: {count: 1, id: "minecraft:carved_pumpkin"}
[13:07:54] [Server thread/INFO]: [surfrock66: Triggered [CustomModelData] (set value to 2)]
[13:07:54] [Render thread/INFO]: [System] [CHAT] Triggered [CustomModelData] (set value to 2)
[13:07:58] [Render thread/INFO]: [System] [CHAT] surfrock66 has the following entity data: {components: {"minecraft:custom_model_data": {floats: [2.0f]}, "minecraft:custom_data": {CustomRoleplayData: 1b}}, count: 1, id: "minecraft:carved_pumpkin"}
[13:08:09] [Server thread/INFO]: [surfrock66: Gave 1 [Carved Pumpkin] to surfrock66]
[13:08:09] [Render thread/INFO]: [System] [CHAT] Gave 1 [Carved Pumpkin] to surfrock66
[13:08:55] [Render thread/INFO]: [System] [CHAT] surfrock66 has the following entity data: {components: {"minecraft:custom_model_data": {strings: ["2"]}}, count: 1, id: "minecraft:carved_pumpkin"}
Using the new custom model format in resourcepacks, the datapack is setting the data incorrectly. It is setting a float for the model ID, when it needs to be strings.
Because you can now name the models by name, it makes it easier and is likely the way this will be handled going forward:
/give @s carved_pumpkin[custom_model_data={strings:["gullopolis_industries_sign"]}] 1
I had written something below about restructuring this to support strings, however I realized after that scoreboard is only integers, which stinks. I'm leaving it just for detail, but minecraft needs a better way than scoreboards to store variables.
It looks like the "copy_data" function could be reworked to indicate "strings" instead of float. I think strings is better because it still works with numerical values if that's how they're defined, and named models is clearly the future:
Command:
/item modify entity @s weapon.mainhand { "function": "minecraft:set_custom_model_data", "strings": { "values": [ "4" ], "mode": "replace_all" } }
I think it would be cleaner if you invoked the item command directly in "set_data.mcfunction" so it didn't rely on the copy_data.json file.
Sorry to spam dev notes in this comment, but where I stumble I hope someone else can pick it up.
The solution I'm working towards is being able to rename an item, then trigger a function (it can be supplemental to CustomModelData and could be CustomModelName) that replaces the model via the string. The best I can come up with is to store the model string in the actual storage namespace, similar to how custom_roleplay_data:config is stored. To that end in my testing, in set_data.mcfunction, I commented out "execute store result" (the first line) and added the following:
data modify storage custom_roleplay_data:data model_name set string entity @s SelectedItem.components."minecraft:custom_name" 1 -1
That gets the item name and stashes the string in storage. Now, the issue is, I can't seem to find the api documentation for getting the string back out in a way that returns a string. Ultimately, some form of this that actually pulls out the string from the data storage:
/item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","strings":{"values":[{"type":"minecraft:storage","target":"custom_roleplay_data:data","path":"model_name"}], "mode": "replace_all"}}
If it helps, all the following result in some kind of "not a string" because I don't see how to reference the storage in the same way you pull data from the scoreboard in copy_data.json:
/item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","strings":{"values":[{"type":"minecraft:storage","target":"custom_roleplay_data:data","path":"model_name"}], "mode": "replace_all"}}
/item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","strings":{"values":[{"type":"minecraft:storage","target":{"target":"custom_roleplay_data:data","path":"model_name"}}], "mode": "replace_all"}}
/item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","strings":{"values":[{"type":"minecraft:storage","target":{"target":"custom_roleplay_data:data"},"path":"model_name"}], "mode": "replace_all"}}
/item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","strings":{"values":[{"type":"minecraft:storage","target":{"custom_roleplay_data:data":"model_name"}}], "mode": "replace_all"}}
the 'values' you're using inside of the item modifier is a json object when it's likely supposed to be a string instead. That's likely why it's giving you an error. If you're trying to copy the data in this manner you'll likely need to use a function macro instead.
In 1.21.3 in my RP (with the 1.5 CRD datapack and my RP not updated to the new format) I can do the following 2 things which both work:
/trigger CustomModelData set 4
/scoreboard objectives add CustomModelTest trigger
/scoreboard players enable @a CustomModelTest
/scoreboard players set @s CustomModelTest 4
/item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","value":{"type":"minecraft:score","target":"this","score":"CustomModelTest"}}
So, I thought in this case, the value expands out? But is that different for strings? I'm going to research it more, but I guess I don't fully expand how the expansion of things works.
My hope is there's a solution; if we all have to redo our RP's to support the float thing, getting a solution to where a second command works with "CustomModelName" instead of "CustomModelData" (for backwards compat) and can be used with names that make sense would be great; the item name is the best thought I have right now.
Ok, I have it working. Let me detail the steps/changes; I think I have some testing/refinement to do, but I can submit a PR depending on if this is a good idea (versus making a separate pack).
This is ultimately NOT fully backwards compatible. For any resource packs before 1.21.4, and thus datapack versions prior to 61, the custom model data is an int and thus the model has to be referenced that way in the resource pack. For anything 1.21.4 and after, it can handle strings or floats, meaning you can choose which way to structure your resource pack. To that end, it's possible to make 2 separate commands in this datapack, one for "/trigger CustomModelData set #" and one for "/trigger CustomModelName" (no args for now), where the prior passes an int and the latter relies on the name of the item. We are dealing with this in the world we're bringing over, which stinks as existing models are borked unless the RP uses the float system, but if we want to use the strings (better user experience) the resource pack will reference the objects that way, and a resource pack can't service both methods for the same item.
So, the changes. First, I added this to init.mcfunction (just below adding the trigger scoreboards):
# Initializes data
data modify storage custom_roleplay_data:name model_name set value null
Then, I modified trigger.mcfunction to initialize the data from the name (so it's set before invoking the function call) and then reference the storage (modifying the last line):
# Set the global custom model name value to use
data modify storage custom_roleplay_data:name model_name set string entity @s
execute if entity @s[nbt={SelectedItem:{components:{"minecraft:custom_data":{CustomRoleplayData:1b}}}}] if score @s crd_xp >= #min_level crd_xp_dummy run function custom_roleplay_data:set_data with storage custom_roleplay_data:name
Lastly, I updated set_data.mcfuntion with the following:
# Change the custom model data
$item modify entity @s weapon.mainhand {"function":"minecraft:set_custom_model_data","strings":{"values":[$(model_name)], "mode": "replace_all"}}
# Clear the global value
data modify storage custom_roleplay_data:name model_name set value null
title @s actionbar [{"text": "Set CustomModelData to ", "color": "green"}, {"score":{ "name": "@s", "objective": "CustomModelData" }, "color": "aqua"}]
Using the function macro, we get the name straight from the storage, and replace the custom model on the item in hand. We then clear the global data value as quickly as the trigger takes to complete. Everything else works exactly the same.
So, in game, I do this:
/give @s minecraft:carved_pumpkin
Then rename the object:
Then type this (the int is just a placeholder that does nothing, in a split "CustomModelName" it would be removed):
/trigger CustomModelData set 0
And voila, the model is set!
I think this has some implicit benefits. Strings is a better way to reference custom models; it makes more sense for the user. Additionally, because you rename the item, the XP penalty is implicit, so you can remove all that from the DP. The custom model persists if you rename the item back afterwards as well.
Now, I have 2 versions of my RP, one supporting the numbers, one supporting strings. The strings one is structured like this (I maintained the numbers as strings just for convenience):
{
"model": {
"type": "select",
"property": "custom_model_data",
"fallback": {
"type": "model",
"model": "block/carved_pumpkin"
},
"cases": [
{ "when": "1", "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_helmet" } },
{ "when": "pinball_helmet", "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_helmet" } },
{ "when": "2", "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_helmet_2" } },
{ "when": "pinball_helmet_2", "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_helmet_2" } },
{ "when": "3", "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_scale" } },
{ "when": "pinball_scale", "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_scale" } },
{ "when": "4", "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_sign_gullopolisindustries" } },
{ "when": "gullopolis_industries_sign", "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_sign_gullopolisindustries" } },
...
And the floats version:
{
"model": {
"type": "range_dispatch",
"property": "custom_model_data",
"fallback": {
"type": "model",
"model": "block/carved_pumpkin"
},
"index": 0,
"entries": [
{ "threshold": 1, "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_helmet" } },
{ "threshold": 2, "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_helmet_2" } },
{ "threshold": 3, "model": { "type": "model", "model": "teh3l3m3nts:item/pinball_scale" } },
{ "threshold": 4, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_sign_gullopolisindustries" } },
{ "threshold": 100, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_andesite" } },
{ "threshold": 101, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_bedrock" } },
{ "threshold": 102, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_blackstone" } },
{ "threshold": 103, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_cobblestone" } },
{ "threshold": 104, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_deepslate" } },
{ "threshold": 105, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_diorite" } },
{ "threshold": 106, "model": { "type": "model", "model": "teh3l3m3nts:item/teh3l3m3nts_block_dirt" } },
...
Basically, you use one format if you want to use strings, and the other if you want to use ints.
I think this has gone beyond what a PR can do and is almost a full fork at this point however I want to do a couple more things before calling it done and pinning a release:
- Make a sample RP which supports all versions going the whole way back as far as custom_model_data existed, handled with overlays so a single RP works (I have tested in 1.21.3 and 1.21.4, and for 1.21.4 I tested both numbers and strings).
- Create a vanilla world for each potential supported version to verify the functionality works as expected.
I completed the fork, but I think it might be too significant to offer a pull request; it takes most of your work and completely re-organizes it so that a single .zip file will work for 1.17.0-1.21.4 AND in 1.21.4 it supports using renamed items with a separate command. I also added documentation to the README and provided sample resource packs for different versions which might help people configure their packs if they're new to this. I also fully tested it in every DP/RP format combination since 1.17.0 to present. https://github.com/surfrock66/custom_roleplay_data