Skip to content

Instantly share code, notes, and snippets.

@Kas-tle
Last active July 31, 2022 11:45
Show Gist options
  • Save Kas-tle/becc11f99e7ce7614bc741b3bac85f7b to your computer and use it in GitHub Desktop.
Save Kas-tle/becc11f99e7ce7614bc741b3bac85f7b to your computer and use it in GitHub Desktop.
Replace heads in world and generate resource pack
# sudo mca2nbt *.mca
# rm *.mca
# sudo chown -R jdcastle:jdcastle *
# sudo chmod 775 *
mkdir -p scratch
mkdir -p target/rp
mkdir -p target/datapack/replace_heads/data/replace_heads/functions/replacements
mkdir -p target/animatronics
mkdir -p target/shops
unzip rp.zip -d target/rp
mkdir -p target/rp/assets/mega/models/heads
mkdir -p target/rp/assets/mega/textures/heads
arr=($(find . -type f -name "*.nbt"))
for f in ${arr[@]}; do nbt2json -b -i "${f}" -o "${f::-4}.json"; done
jq -s -r '
([
.[] | .nbt? | .[]? | .value? | .[]? | .value? | .list? | .[]?
| select(.[]? | .name? == "HandItems")
| select(.[]? | .value? | .list? |.[]? | .[]? | .value? | .[]? | .name? == "SkullOwner")
| select(.[]? | .value? | .list? |.[]? | .[]? | .value? | .[]? | .value? | .[]? | .value? | .[]? | .name? == "textures")
| select(.[]? | .value? | .list? |.[]? | .[]? | .value? | .[]? | .value? | .[]? | .value? | .[]? | .value? | .list? | .[]? | .[]? | .name? == "Value")
| del(..| objects | select(.name == "ArmorItems"))
] + [
.[] | .nbt? | .[]? | .value? | .[]? | .value? | .list? | .[]?
| select(.[]? | .name? == "ArmorItems")
| select(.[]? | .value? | .list? |.[]? | .[]? | .value? | .[]? | .name? == "SkullOwner")
| select(.[]? | .value? | .list? |.[]? | .[]? | .value? | .[]? | .value? | .[]? | .value? | .[]? | .name? == "textures")
| select(.[]? | .value? | .list? |.[]? | .[]? | .value? | .[]? | .value? | .[]? | .value? | .[]? | .value? | .list? | .[]? | .[]? | .name? == "Value")
| del(..| objects | select(.name == "HandItems"))
]
| map(
(.[]? | .value? | .list?) as $armor_items |
(.[]? | select(.value? | .list? | .[]? | .[]? | .value? | .[]? | .name? == "SkullOwner") | .name?) as $slot_type |
($armor_items? | to_entries? | .[]? | select(.value? | .[]? | .[]? | .[]? | .name? == "SkullOwner") | .key?) as $slot_index |
($armor_items? | .[]? | .[]? | .value? | .[]? | select(.name? == "SkullOwner") | .value? | .[]? | .value? | .[]? | select(.name? == "textures") | .value? | .list? | .[]? | .[]? | select(.name? == "Value") | .value?) as $skin_data |
{
"value": ($skin_data),
"pos": (.[]? | select(.name? == "Pos") | .value? | .list?),
"slot_type": ($slot_type),
"slot_index": ($slot_index)
})) as $head_data
| ([($head_data | .[]? | .value)] | unique) as $unique_heads
| (($unique_heads | to_entries) | map({(.value): (.key + 1)}) | add) as $head_index
| $head_data | map(
.value as $value
| {
"head_index": ($head_index | .[$value]),
"skin_data": (.value),
"slot_type": (.slot_type),
"slot_index": (.slot_index),
"chunk": (.pos | map(. | floor)),
"pos": (.pos)
}
) | unique
' **/*.json > scratch/heads.json
jq -n '
{
"textures": {
"0": "minecraft:block/white_concrete",
"particle": "minecraft:block/white_concrete"
},
"elements": [
{
"name": "head",
"from": [0.5, 0.5, 0.5],
"to": [15.5, 15.5, 15.5],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -23.5, 8]},
"faces": {
"north": {"uv": [4, 4, 8, 8], "texture": "#0"},
"east": {"uv": [0, 4, 4, 8], "texture": "#0"},
"south": {"uv": [12, 4, 16, 8], "texture": "#0"},
"west": {"uv": [8, 4, 12, 8], "texture": "#0"},
"up": {"uv": [8, 4, 4, 0], "texture": "#0"},
"down": {"uv": [8, 0, 12, 4], "texture": "#0"}
}
},
{
"name": "headwear",
"from": [0, 0, 0],
"to": [16, 16, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -23.5, 8]},
"faces": {
"north": {"uv": [4, 12, 8, 16], "texture": "#0"},
"east": {"uv": [0, 12, 4, 16], "texture": "#0"},
"south": {"uv": [12, 12, 16, 16], "texture": "#0"},
"west": {"uv": [8, 12, 12, 16], "texture": "#0"},
"up": {"uv": [8, 12, 4, 8], "texture": "#0"},
"down": {"uv": [8, 8, 12, 12], "texture": "#0"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, 45, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"thirdperson_lefthand": {
"rotation": [75, 45, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, 45, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, 225, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, 225, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"scale": [0.5, 0.5, 0.5]
}
},
"groups": [
{
"name": "head",
"origin": [0, 24, 0],
"color": 0,
"children": [0]
},
{
"name": "headwear",
"origin": [0, 24, 0],
"color": 0,
"children": [1]
}
]
}
' > target/rp/assets/mega/models/heads/head_template.json
jq -n '
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "minecraft:item/gold_ingot"
},
"overrides": []
}
' > target/rp/assets/minecraft/models/item/gold_ingot.json
jq -n '
{
"pack": {
"pack_format": 5,
"description": "Convert armor stand heads"
}
}
' > target/datapack/replace_heads/pack.mcmeta
jq -r '[.[] | [.head_index, .skin_data]] | unique | .[] | @tsv | gsub("\\t";",")' scratch/heads.json > scratch/heads_resource.csv
while IFS=, read -r head_index skin_data; do
SKIN_URL=$(echo "${skin_data}" | base64 -d | jq -r .textures.SKIN.url)
echo "Downloading head ${head_index}: ${SKIN_URL}"
wget -O "scratch/scratchhead.png" "${SKIN_URL}" -q --show-progress
convert "scratch/scratchhead.png" -crop 32x16+0+0 "scratch/scratchhead_1.png"
convert "scratch/scratchhead.png" -crop 32x16+32+0 "scratch/scratchhead_2.png"
convert -append scratch/scratchhead_*.png "target/rp/assets/mega/textures/heads/customhead_${head_index}.png"
rm scratch/scratchhead_*.png && rm "scratch/scratchhead.png"
jq -n --arg head_index "${head_index}" '
{
"parent": "mega:heads/head_template",
"textures": {
"0": ("mega:heads/customhead_" + $head_index),
"particle": ("mega:heads/customhead_" + $head_index)
}
}
' > "target/rp/assets/mega/models/heads/customhead_${head_index}.json"
jq --arg head_index "${head_index}" '
.overrides += [{"predicate": {"custom_model_data": ($head_index | tonumber)}, "model": ("mega:heads/customhead_" + $head_index)}]
' target/rp/assets/minecraft/models/item/gold_ingot.json | sponge target/rp/assets/minecraft/models/item/gold_ingot.json
done < scratch/heads_resource.csv
jq -r '[.[] | [.head_index, .slot_type, .slot_index, .chunk[0], .chunk[2], .pos[0], .pos[1], .pos[2]]] | .[] | @tsv | gsub("\\t";",")' scratch/heads.json > scratch/heads_replace.csv
counter=0
while IFS=, read -r head_index slot_type slot_index chunk_x chunk_z pos_x pos_y pos_z; do
# NOTE:
counter=$(( $counter + 1 ))
next_counter=$(( $counter + 1 ))
echo "execute in minecraft:overworld run forceload add $(( $chunk_x - 2 )) $(( $chunk_z - 2 )) $(( $chunk_x + 2 )) $(( $chunk_z + 2 ))" >> target/datapack/replace_heads/data/replace_heads/functions/replacements/start.mcfunction
cat <<EOF >> target/datapack/replace_heads/data/replace_heads/functions/replacements/${counter}.mcfunction
execute in minecraft:overworld run data modify entity @e[type=armor_stand,limit=1,sort=nearest,x=${pos_x},y=${pos_y},z=${pos_z},distance=..1] ${slot_type}[${slot_index}] set value {id:"minecraft:gold_ingot",Count:1b,tag:{CustomModelData:${head_index}}}
execute in minecraft:overworld run forceload remove ${chunk_x} ${chunk_z}
schedule function replace_heads:replacements/${next_counter} 5t append
EOF
done < scratch/heads_replace.csv
echo "schedule function replace_heads:replacements/1 10s append" >> target/datapack/replace_heads/data/replace_heads/functions/replacements/start.mcfunction
echo 'execute in minecraft:overworld run forceload remove all' >> target/datapack/replace_heads/data/replace_heads/functions/replacements/${counter}.mcfunction
echo 'execute in minecraft:overworld run say Done with heads' >> target/datapack/replace_heads/data/replace_heads/functions/replacements/${counter}.mcfunction
yq -r -s '[.[]? | .Animatronic | .[]? | .clothes? | .[]? | select(.. ."meta-type"? == "SKULL") |.. .internal?] - [null] | unique[]' animatronics/*.anima > scratch/anima_nbt.csv
jq -n '{}' > scratch/anima_nbt_index.json
while IFS=, read -r base_64_nbt; do
base_64_skin_data=$(echo "${base_64_nbt}" | base64 --decode | nbt2json -b | jq -r '.nbt? | .[0]? | .value[0]? | .value[]? | select(.name? == "Properties") | .value[0]? | .value? | .list[0]?[]? | select(.name? == "Value") | .value?')
jq --arg base_64_nbt "${base_64_nbt}" --arg base_64_skin_data "${base_64_skin_data}" '. += {($base_64_nbt): ($base_64_skin_data)}' scratch/anima_nbt_index.json | sponge scratch/anima_nbt_index.json
done < scratch/anima_nbt.csv
for f in animatronics_2/*.anima; do
yq -y --slurpfile head_slurp_file scratch/heads.json --slurpfile nbt_slurp_file scratch/anima_nbt_index.json '
($head_slurp_file | .[]? | map ({(.skin_data): (.head_index)}) | add) as $head_index |
($nbt_slurp_file | .[]?) as $nbt_index |
(.Animatronic | keys[0]) as $anima_key | (.Animatronic | .[($anima_key)].clothes)
|= map_values(if (.meta? | .["meta-type"]? == "SKULL") then (
.type |= "GOLD_INGOT" |
.meta |= {
"==": "ItemMeta",
"meta-type": "UNSPECIFIC",
"custom-model-data": ((.internal as $index | $nbt_index | .[$index]) as $head_base_64 | $head_index | .[$head_base_64] // 1)
}
) elif (.[]? | .meta? | .["meta-type"]? == "SKULL") then (
map_values(
if (.meta? | .["meta-type"]? == "SKULL") then (
.type |= "GOLD_INGOT" |
.meta |= {
"==": "ItemMeta",
"meta-type": "UNSPECIFIC",
"custom-model-data": ((.internal as $index | $nbt_index | .[$index]) as $head_base_64 | $head_index | .[$head_base_64] // 1)
}
) else . end
)
) else . end)
' ${f} > "target/animatronics/${f##*/}"
done
for f in shops/*.yml; do
yq -y --slurpfile head_slurp_file scratch/heads.json '
($head_slurp_file | .[]? | map ({(.skin_data): (.head_index)}) | add) as $head_index |
keys[0] as $key | (.[$key] | .items?) |= map_values(if (.item?.material? == "PLAYER_HEAD") then
(.item?.skin? as $skin
| .item?.material? |= "GOLD_INGOT"
| .item?.model? |= ($head_index | .[$skin] // 1)
| del(.item?.skin?)
) else
. end)
' ${f} > "target/shops/${f##*/}"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment