Skip to content
ShadowMC

Configuration

ShadowCustomizer Models

The 3D models for what ShadowCustomizer invents, delivered on their own.

Extension v1.0.4

Where the files are

plugins/ShadowCustomizerModels/

The plugin creates them on first start and updates them by itself on new versions, keeping your changes.

Applying changes

/scmodels reload

Reloads the files without restarting the server.

What’s inside

5 files · 136 settings

Start with: config.yml

Settings

The files that decide how the plugin behaves. Start here.

config.yml

General settings
25 settings
plugins/ShadowCustomizerModels/config.yml
File notes

This is the 3D models extension for ShadowCustomizer: it builds the resource pack, merges it with the one you already have, and serves it to your players.

The folders that matter, inside this one:

models/        how every item and block looks (the .yml files)
textures/      the .png images, laid out like the pack itself
model-files/   models already made in Blockbench, if you have any
pack-extra/    YOUR OWN FILES: a resource pack .zip, an unzipped
               pack, or loose assets/... files. Whatever is in
               here is added to the pack at every build. See the
               README.txt inside.
pack/          GENERATED HERE. Do not put anything in by hand:
               this folder is emptied on every build.
pack.zip       the compressed pack, ready to be served

core

core1 key
server-ip''

The address this server is known by from the outside. Only the licence needs it. Empty is fine for almost everyone.

messages

messages2 keys
default-localeen_US

The language used when the player's own is not available. The plugin ships en_US and it_IT; a file you add to lang/ is picked up too.

follow-client-localetrue

Every player reads the messages in their own client language, when that file exists.

pack - the pack that gets built

pack9 keys
namespaceshadow

The namespace of the models. It MUST match models.namespace in ShadowCustomizer's config: that is what ties "shadow:sword" written in an item definition to the file inside the pack. If the two do not match, the client cannot find the model and draws the purple and black missing-model cube.

format88

The pack_format, that is, which Minecraft version this pack is for.

Getting it wrong makes the client reject the ENTIRE pack, silently: it is the first place to look when "the pack will not load".

88 is the value for 1.26.2. It changes with every Minecraft version, and the way to know it for certain is to read it from the server itself: inside the version's jar there is a version.json with a "pack_version", and the number you want is "resource_major".

description'ShadowCustomizer content'

What you read in the client's resource pack list.

The merge - the reason this extension exists

pack
base''

If you already have a resource pack of your own, put the path of its UNPACKED folder here - the one containing pack.mcmeta. The builder will copy yours first and only write on top of it what it adds.

Keep reading

There is one rule to the merge: WHOEVER WAS THERE FIRST WINS. Your pack is your work; ours is what gets added. Where the two say different things about the same field, yours stands and the conflict is reported - with the file and the field, so you can go and look.

Your pack is NEVER modified: it is read and copied.

The path can be absolute or relative to this folder. Empty means no merge.

It is a Premium feature. Without it the pack is still built, just separate from yours.

Not the same thing as the pack-extra/ folder: pack.base is a pack you keep elsewhere and whose files WIN, merged field by field; pack-extra/ is the drop-in folder, where the generated files win (see extra-overrides below). Most people only need pack-extra/.

extra-overridesfalse

Who wins when a file in pack-extra/ has the same path as a file the plugin generates (a model, the note_block blockstate, an item definition).

false: the generated one is kept, and the console tells you which
       file it was. Right for almost everyone.
true:  yours replaces it, and the console says that too. Careful:
       replacing a generated model breaks the items that use it.

Adding the files of pack-extra/ is part of the merge, a Premium feature.

url''

The full link clients download the pack from.

LEAVE IT EMPTY unless you upload pack.zip somewhere yourself: then the built-in server below serves it and the link is worked out on its own. Write it when you host the pack elsewhere (a website, a CDN, a shared host that gives you no port for the built-in server): it always wins over everything else. After every build, upload the new pack.zip again.

generate-on-starttrue

Rebuild the pack on every start.

True is almost always right: a server coming back up should find the pack already up to date, and asking an administrator to type a command after every restart is exactly the loop that makes resource packs go stale. The build runs off the game thread and does not slow the start-up down.

send-on-jointrue

Send the pack to whoever joins.

True out of the box: custom items and blocks without their pack look wrong, and the point of this extension is that players see them with nothing to configure. The pack goes out with its SHA-1 hash, so a player who already has it does not download it again. Whoever joins while the pack is still being built right after a restart gets it as soon as it is ready.

It is a Premium feature.

requiredfalse

Whether players MUST accept the pack.

false: they can decline and play without it (they will see the
       custom items as the material they are made of).
true:  the game only offers "accept" or "disconnect".

The text on the download screen is not here: it is the key pack.prompt in lang/<language>.yml, so every player reads it in their own language. Empty ('') means no text.

server - the server that delivers the pack

server8 keys

The alternative is uploading a file tens of megabytes big to a hosting provider and remembering to do it again after every change. That loop is why half the servers with custom content are running a pack six months out of date.

It is a Premium feature. Without it the pack sits in pack.zip next door and you serve it however you like.

enabledtrue

On out of the box: it is what gets the pack to your players with nothing configured. Turn it off only if you upload pack.zip yourself and write its link in pack.url.

modeauto

Where the pack is served.

Keep reading
auto            the Minecraft port when players reach this server
                directly; its own port (server.port, below) when a
                proxy sits in front, when public-address below
                carries a port other than the Minecraft one (the
                port you forwarded for the pack server), or when
                this server does not let plugins share its port.
                Right for almost everyone: the console says which
                one it picked.
minecraft-port  always the Minecraft port (server-port in
                server.properties). Nothing to open.
own-port        its own small web server on server.port, as up to
                version 1.0.2.

On the Minecraft port only a download of the current pack is answered (the link carries the pack's SHA-1 hash, anything else gets a 404); every other connection goes on to Minecraft untouched, and players notice nothing.

The Minecraft port CANNOT work when players do not connect to this server directly:

- a BungeeCord or Velocity proxy in front: players connect to the
  proxy, not here. auto notices it (bungeecord in spigot.yml,
  velocity or proxy-protocol in config/paper-global.yml) and uses
  its own port;
- a DDoS protection service in front (TCPShield and the like) that
  only lets Minecraft traffic through. That one cannot be seen from
  here: set own-port and open server.port, or upload pack.zip
  somewhere and use pack.url.
port8123

The port of the pack's own server (TCP), used by own-port - and by auto when it falls back to it. It is NOT the Minecraft port.

Keep reading

It has to be reachable from the internet if your players come from outside: allow it in the firewall and, on a home connection, forward it on the router to this machine. The console reminds you of it at every start, with the link.

On a shared host (Pterodactyl and the like) only the ports they give you are open: ask for an extra port - often called an "allocation" - and write it here. If they give you none, upload pack.zip somewhere and use pack.url instead.

If the port is already taken the console says so and the plugin carries on without the server: pick another number.

bind''

Which network interface the pack's own server listens on (own-port). Empty means all of them, which is what you want nearly every time. On the Minecraft port it is Minecraft's own: server-ip in server.properties.

local-players-use-lantrue

Players on the same computer or the same home network as the server get a local link (127.0.0.1 or the 192.168.x.x address) instead of the public one: almost no home router lets you reach your own public IP from inside, so the download would fail for you and for anyone playing from your house. Players from outside keep getting the public link. Set false only if a proxy on this same machine hides the players' real addresses (then everybody looks like 127.0.0.1).

public-address''

The address to put in the download link.

Keep reading

EMPTY IS RIGHT FOR ALMOST EVERYONE: the plugin finds it by itself, in this order, and the console says which one it used:

1. server.bind above, if it is a public IP;
2. server-ip in server.properties, if it is a public IP;
3. asking the internet (public-ip-lookup below);
4. this machine's address on the local network - that works only
   for players on the same network, and the console warns you.

Write it when you have a domain (play.example.com), a proxy in front, or when from outside the port has a different number than the one the pack is served on (play.example.com:30001). A port written here always wins: on the Minecraft port it is the one players type to join, on the pack's own port the one mapped to server.port.

public-ip-lookuptrue

Ask the internet for this server's public IP, when nothing above gives it.

Once at start and after /scmodels reload, in the background, a few seconds at most. It is a plain HTTPS request to the services below, which answer with the IP they see: nothing about your server is sent. It is skipped when public-address or pack.url is written, or when server-ip is already public.

false = never ask: the link then uses the local-network address, unless you write public-address.

public-ip-services["https://shadow-licences.riccardobotins.worke…]

Who to ask, in order: the next one is tried if one does not answer. Any service that answers with the bare IP works. The default is the ShadowMC licence service (the same as license.activation-url), which answers with the address it sees and nothing else.

Licence

This extension is sold on its own, so it has its own key: the one for ShadowCustomizer does not unlock it, and that is by design - each product is signed by a pair only that product knows.

license3 keys
activation-url'https://shadow-licences.riccardobotins.workers.dev'

The base address of the activation service. The plugin appends /claim and /check on its own.

This is OUR service and it is filled in for you: buying and activating have to be the same gesture. Left empty - which is how this file shipped until 20/09/2026 - "/scmodels license claim <code>" has nobody to ask, and a paying customer is left holding a code that does nothing. Change it only if you run your own copy of the service.

grace-days7

How many days the plugin keeps working after the service stops answering. It is not generosity: a subscription that just renewed, a DNS hiccup or an outage on our side must never take Premium away from someone who paid.

resolve-public-iptrue

Asks the activation service above (activation-url + /ip) how this server looks from outside: it answers with the address it sees, and nothing about the server is sent. It is the only way a machine behind a NAT can know the address its players use. Only when a licence is installed: a server without a key asks nobody. false = never ask.

bStats (anonymous usage statistics, https://bStats.org)

What is sent, every 30 minutes: a random server id (serverUuid, not derived from anything), the plugin version, the Minecraft/Paper and Java versions, the operating system and core count, the online mode, how many players are online (a number only) and the licence tier (free/premium/creator). NO player data: no names, no UUIDs, no addresses.

Keep reading

false switches off this plugin only. enabled: false in plugins/bStats/config.yml switches off every bStats plugin on the server at once.

metrics1 key
enabledtrue

equipment.yml

How worn armour looks
3 settings
plugins/ShadowCustomizerModels/equipment.yml
File notes

This is the file that fixes the thing everyone hits: a custom armour set that looks perfect in your hand and netherite on your back.

WHY THAT HAPPENS. A model is how an ITEM is drawn - in your hand, in the inventory, on the ground. The moment the armour is put ON, the game stops drawing that model: it lays two texture layers over the player's body instead. Which layers to use is not written in the item and not in the model. It is a separate thing - an equipment asset - and without one the game falls back to the material's.

Every entry here becomes one of those assets inside the pack, and an item claims it by writing the same name in its 'equipment' line:

shadowsteel_chestplate:
  material: NETHERITE_CHESTPLATE
  model: 'shadow:shadowsteel_chestplate'     <- in mano
  equipment: 'shadow:shadowsteel'            <- addosso

THE LAYERS. 'humanoid' is the body, the arms and the head; 'humanoid_leggings' is the legs, which the game keeps separate because that geometry is its own; 'wings' is the elytra, which is neither. A piece that only needs one declares one.

AN ELYTRA IS THE SAME STORY as the chestplate: a model alone gives you custom wings in the inventory and vanilla wings in flight, because in flight the game is drawing the 'wings' layer and not the model. That is what shadowsteel_wings below is for.

THE IMAGES go in textures/entity/equipment/<layer>/<name>.png, with the same name as the entry here. They are ordinary armour layer textures, the same shape vanilla uses. They are copied into the pack with all the others.

THE HEAD IS THE EXCEPTION. On the head slot the game draws the ITEM'S MODEL, not a layer. A helmet with a 3D model is worn exactly as it looks in your hand - it is how hats work in resource packs - so a helmet needs nothing here to show its model. It still uses this file if you want the classic layer look instead.

config-version1
equipment2 keys
equipment.shadowsteel1 key

Shadowsteel: body and legs.

layers[humanoid, humanoid_leggings]
equipment.shadowsteel_wings1 key

The elytra's wings. A layer of their own: the geometry of the wings is not the same as the body's.

layers[wings]

models/content.yml

How the shipped content looks
108 settings
plugins/ShadowCustomizerModels/models/content.yml
File notes

Every id here matches an id in ShadowCustomizer's items/ and blocks/ folders. This file only says how a thing is DRAWN; what it does is in the other plugin.

Two kinds of entry:

file:      a model made in Blockbench, sitting in model-files/. The
           builder copies it as it is and writes everything around
           it. Every weapon and every piece of armour below is one
           of these: they are real 3D models, not flat sprites.
texture:   a flat image in textures/. Three lines and you are done.
           The ores and the raw drops are these.

Keys are English, like every key in every file.

config-version1

Ores - the blocks in the world

antracite_ore3 keys

type: block writes the variant in the note block blockstate as well, which is what makes the block look different from a note block.

typeblock
texture'block/antracite_ore'
parent'block/cube_all'
arcadium_ore3 keys
typeblock
texture'block/arcadium_ore'
parent'block/cube_all'
elektron_ore3 keys
typeblock
texture'block/elektron_ore'
parent'block/cube_all'
oricalco_ore3 keys
typeblock
texture'block/oricalco_ore'
parent'block/cube_all'

Ores - raw and refined

antracite_raw3 keys

item/generated is the parent of every flat item. It is the default, written out here so the pattern is visible.

typeitem
texture'item/antracite_raw'
parent'item/generated'
antracite_dust3 keys
typeitem
texture'item/antracite_dust'
parent'item/generated'
arcadium_raw3 keys
typeitem
texture'item/arcadium_raw'
parent'item/generated'
arcadium_ingot3 keys
typeitem
texture'item/arcadium_ingot'
parent'item/generated'
elektron_raw3 keys
typeitem
texture'item/elektron_raw'
parent'item/generated'
elektron_ingot3 keys
typeitem
texture'item/elektron_ingot'
parent'item/generated'
oricalco_raw3 keys
typeitem
texture'item/oricalco_raw'
parent'item/generated'
oricalco_rod3 keys
typeitem
texture'item/oricalco_rod'
parent'item/generated'

Shadowsteel - the first weapon set, 3D models

shadowsteel_sword2 keys

These declare their own textures inside the JSON, so there is nothing to write here beyond the file name. The textures they ask for are in textures/item/ with the paths the model expects.

typeitem
file'shadowsteel_sword.json'
shadowsteel_axe2 keys
typeitem
file'shadowsteel_axe.json'
shadowsteel_pickaxe2 keys
typeitem
file'shadowsteel_pickaxe.json'
shadowsteel_bow3 keys
typeitem
file'shadowsteel_bow.json'
pulling
[shadowsteel_bow_0, shadowsteel_bow_1, shadowsteel_bow_2]+1

The three draw stages. Without this line the bow stays still while you draw it: the pack knows only one model and the game draws that one. The thresholds are vanilla's, so it bends with the rhythm of every other bow - a different rhythm gets noticed, and it gets noticed as a defect.

shadowsteel_crossbow5 keys
typeitem
file'shadowsteel_crossbow.json'
pulling
[shadowsteel_crossbow_0, shadowsteel_crossbow_1, shadowsteel_crossbow_2]+1

A crossbow needs six: three for charging, plus unloaded, loaded with an arrow and loaded with a rocket.

chargedshadowsteel_crossbow_charged
fireworkshadowsteel_crossbow_firework
shadowsteel_trident2 keys
typeitem
file'shadowsteel_trident.json'
shadowsteel_shield3 keys
typeitem
file'shadowsteel_shield.json'
blockingshadowsteel_shield_blocking

The blocking pose. Without it, the raised shield looks the same as the lowered shield.

Shadowsteel - the armour

shadowsteel_helmet2 keys

Helmet and chestplate have a 3D model; leggings and boots are flat, because the set they came from has no model for those two.

What these say is how the ITEM looks. How the set looks WORN is in equipment.yml next door: the game lays texture layers over the body once armour is on and stops drawing the item model. The helmet is the exception - on the head the item's own model is what you see.

typeitem
file'shadowsteel_helmet.json'
shadowsteel_chestplate3 keys

The chestplate has no 3D model, and that is not an oversight: the set the armour comes from does not have one. What looked like its model was the model of a CHEST, and that is exactly what was showing in the inventory.

It is not needed. A chestplate in the hand is seen for half a second; what you look at all the time is how it looks WORN, and equipment.yml handles that with its layers - which in fact was already working.

typeitem
texture'item/shadowsteel_chestplate'
parent'item/generated'
shadowsteel_leggings3 keys
typeitem
texture'item/shadowsteel_leggings'
parent'item/generated'
shadowsteel_boots3 keys
typeitem
texture'item/shadowsteel_boots'
parent'item/generated'

Darkflame - the second weapon set, 3D models and animated textures

darkflame_sword2 keys

This set carries .mcmeta files next to some of its textures: those are animation descriptors, and they are copied into the pack with the images. It is why the flames on these move.

typeitem
file'darkflame_sword.json'
darkflame_axe2 keys
typeitem
file'darkflame_axe.json'
darkflame_pickaxe2 keys
typeitem
file'darkflame_pickaxe.json'
darkflame_bow3 keys
typeitem
file'darkflame_bow.json'
pulling
[darkflame_bow_0, darkflame_bow_1, darkflame_bow_2]+1
darkflame_spear2 keys
typeitem
file'darkflame_spear.json'
darkflame_shield3 keys
typeitem
file'darkflame_shield.json'
blockingdarkflame_shield_blocking
ranger_crossbow2 keys
typeitem
file'ranger_crossbow.json'

Boss heads

darkflame_helmet2 keys

A mob wears these as a helmet. It is how a mob gets a custom look without a separate model plugin: the head you see is an item with a 3D model on the mob's head slot.

typeitem
file'darkflame_helmet.json'
pumpkin_king_head2 keys
typeitem
file'pumpkin_king_head.json'

The elytra

shadowsteel_elytra2 keys

The model is the item in your hand. The wings you fly with are not here: they are a worn look, and they live in equipment.yml.

typeitem
file'shadowsteel_elytra.json'

Furniture

runic_chest2 keys

type: block with a Blockbench file, which is the combination that makes furniture possible: a custom block is not obliged to be a cube. The model is drawn in the world exactly as it was drawn.

The block underneath is still a whole note block, so the hitbox is a full cube whatever the shape says. See blocks/furniture.yml.

typeblock
file'runic_chest.json'
crypt_tombstone2 keys
typeblock
file'crypt_tombstone.json'
steel_warden_model2 keys

The body of the Steel Warden (mobs.yml): worn by a display on the mob, not by a player.

typeitem
file'steel_warden_model.json'

The frames

shadowsteel_bow_0{ type: item, file: 'shadowsteel_bow_0.json' }

Ordinary models, declared like any other. They are never worn on their own: the entries above name them, and the pack says when to draw each.

They are listed apart because that is what they are - frames of an animation, not items - and because a bow set has three of them and it would bury the weapon it belongs to.

shadowsteel_bow_1{ type: item, file: 'shadowsteel_bow_1.json' }
shadowsteel_bow_2{ type: item, file: 'shadowsteel_bow_2.json' }
shadowsteel_crossbow_0
{ type: item, file: 'shadowsteel_crossbow_0.json' }+1
shadowsteel_crossbow_1
{ type: item, file: 'shadowsteel_crossbow_1.json' }+1
shadowsteel_crossbow_2
{ type: item, file: 'shadowsteel_crossbow_2.json' }+1
shadowsteel_crossbow_charged
{ type: item, file: 'shadowsteel_crossbow_charged.json' }+1
shadowsteel_crossbow_firework
{ type: item, file: 'shadowsteel_crossbow_firework.json' }+1
shadowsteel_shield_blocking
{ type: item, file: 'shadowsteel_shield_blocking.json' }+1
darkflame_bow_0{ type: item, file: 'darkflame_bow_0.json' }
darkflame_bow_1{ type: item, file: 'darkflame_bow_1.json' }
darkflame_bow_2{ type: item, file: 'darkflame_bow_2.json' }
darkflame_shield_blocking
{ type: item, file: 'darkflame_shield_blocking.json' }+1

Languages

The texts the plugin shows to players, one file per language.

lang/

Texts and messages
2 files
plugins/ShadowCustomizerModels/lang/
File notes

ShadowCustomizerModels - english

Every message the plugin shows to players, one file per language. The keys are not listed one by one: here are the files, how many texts they hold and the placeholders they use.

FileLanguageTexts
lang/en_US.yml reference English 166
lang/it_IT.yml Italiano 166

Most used placeholders

The plugin replaces them with the right value: leave them written as they are.

  • <prefix>
  • <label>
  • <path>
  • <model>
  • <port>
  • <count>
  • <source>
  • <reason>
  • <url>
  • <id>
  • <player>
  • <error>
  • <files>
  • <state>

Message groups

  • prefix 1
  • common 6
  • command 5
  • confirm 6
  • help 7
  • pack 21
  • report 34
  • server 6
  • status 13
  • reload 2
  • address 7
  • log 23
  • license 35

All the documentation