Configuration
ShadowCustomizer Models
The 3D models for what ShadowCustomizer invents, delivered on their own.
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.
No setting found
Try another word, or search every plugin.
Settings
The files that decide how the plugin behaves. Start here.
config.yml
General settings 25 settings
config.ymlplugins/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 servedDo i have to write an address, a link, or open a port?
Normally: NO. Out of the box the plugin does the whole job:
Keep reading
1. it builds pack.zip at every start (and on /scmodels generate) 2. it serves it on the Minecraft port itself: players download it from the same address and port they play on 3. it works out the address players reach this machine at 4. it sends the pack to every player who joins
Nothing to open: the port your players already use is the one the pack travels on. The console prints the link at every start.
The exception is a server players do not reach directly - a BungeeCord/Velocity proxy, or a DDoS protection service, in front. There the pack gets its own port, 8123 (TCP) by default, which must be reachable from the internet: see server.mode below.
Steps 2 and 4, and pack-extra/, are Premium features. On the free edition the pack is still built: upload pack.zip somewhere, write its link in pack.url, and send it with /scmodels send.
The address strings below exist for the cases where the automatic way is not the right one, and you can leave them all empty
otherwise:
pack.url you upload pack.zip yourself (web hosting,
a CDN). The full link. It always wins.
server.public-address the address found automatically is wrong:
you have a domain, a proxy in front, or
the port is mapped to another number.
server.bind the machine has several network cards and
the pack's own server must listen on one
only (own port only).A wrong value never stops anything: it is replaced with the default and reported in console.
UPDATES: when a new version changes this file (or the lang/, models/content.yml and equipment.yml files), your copy is moved to the new version automatically: everything you changed is kept, the values you never touched take the new defaults. Your old files are in backup/<date>/, and if something needs your attention you will find PORTING-<version>.txt in this folder.
EVERY KEY IN EVERY FILE IS ENGLISH. What gets translated is what a player reads, and that lives in lang/.
config-version1core
core1 keyserver-ip''The address this server is known by from the outside. Only the licence needs it. Empty is fine for almost everyone.
messages
messages2 keysdefault-localeen_USThe 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-localetrueEvery player reads the messages in their own client language, when that file exists.
pack - the pack that gets built
pack9 keysnamespaceshadowThe 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.
format88The 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
packbase''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-overridesfalseWho 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-starttrueRebuild 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-jointrueSend 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.
requiredfalseWhether 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 keysThe 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.
enabledtrueOn 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.
modeautoWhere 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.
port8123The 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-lantruePlayers 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-lookuptrueAsk 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 keysactivation-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-days7How 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-iptrueAsks 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 keyenabledtrue equipment.yml
How worn armour looks 3 settings
equipment.ymlplugins/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-version1equipment2 keysequipment.shadowsteel1 keyShadowsteel: body and legs.
layers[humanoid, humanoid_leggings]equipment.shadowsteel_wings1 keyThe 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
models/content.ymlplugins/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-version1Ores - the blocks in the world
antracite_ore3 keystype: block writes the variant in the note block blockstate as well, which is what makes the block look different from a note block.
typeblocktexture'block/antracite_ore'parent'block/cube_all'arcadium_ore3 keystypeblocktexture'block/arcadium_ore'parent'block/cube_all'elektron_ore3 keystypeblocktexture'block/elektron_ore'parent'block/cube_all'oricalco_ore3 keystypeblocktexture'block/oricalco_ore'parent'block/cube_all'Ores - raw and refined
antracite_raw3 keysitem/generated is the parent of every flat item. It is the default, written out here so the pattern is visible.
typeitemtexture'item/antracite_raw'parent'item/generated'antracite_dust3 keystypeitemtexture'item/antracite_dust'parent'item/generated'arcadium_raw3 keystypeitemtexture'item/arcadium_raw'parent'item/generated'arcadium_ingot3 keystypeitemtexture'item/arcadium_ingot'parent'item/generated'elektron_raw3 keystypeitemtexture'item/elektron_raw'parent'item/generated'elektron_ingot3 keystypeitemtexture'item/elektron_ingot'parent'item/generated'oricalco_raw3 keystypeitemtexture'item/oricalco_raw'parent'item/generated'oricalco_rod3 keystypeitemtexture'item/oricalco_rod'parent'item/generated'Shadowsteel - the first weapon set, 3D models
shadowsteel_sword2 keysThese 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.
typeitemfile'shadowsteel_sword.json'shadowsteel_axe2 keystypeitemfile'shadowsteel_axe.json'shadowsteel_pickaxe2 keystypeitemfile'shadowsteel_pickaxe.json'shadowsteel_bow3 keystypeitemfile'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 keystypeitemfile'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_chargedfireworkshadowsteel_crossbow_fireworkshadowsteel_trident2 keystypeitemfile'shadowsteel_trident.json'shadowsteel_shield3 keystypeitemfile'shadowsteel_shield.json'blockingshadowsteel_shield_blockingThe blocking pose. Without it, the raised shield looks the same as the lowered shield.
Shadowsteel - the armour
shadowsteel_helmet2 keysHelmet 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.
typeitemfile'shadowsteel_helmet.json'shadowsteel_chestplate3 keysThe 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.
typeitemtexture'item/shadowsteel_chestplate'parent'item/generated'shadowsteel_leggings3 keystypeitemtexture'item/shadowsteel_leggings'parent'item/generated'shadowsteel_boots3 keystypeitemtexture'item/shadowsteel_boots'parent'item/generated'Darkflame - the second weapon set, 3D models and animated textures
darkflame_sword2 keysThis 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.
typeitemfile'darkflame_sword.json'darkflame_axe2 keystypeitemfile'darkflame_axe.json'darkflame_pickaxe2 keystypeitemfile'darkflame_pickaxe.json'darkflame_bow3 keystypeitemfile'darkflame_bow.json'pulling[darkflame_bow_0, darkflame_bow_1, darkflame_bow_2]+1
darkflame_spear2 keystypeitemfile'darkflame_spear.json'darkflame_shield3 keystypeitemfile'darkflame_shield.json'blockingdarkflame_shield_blockingranger_crossbow2 keystypeitemfile'ranger_crossbow.json'Boss heads
darkflame_helmet2 keysA 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.
typeitemfile'darkflame_helmet.json'pumpkin_king_head2 keystypeitemfile'pumpkin_king_head.json'The elytra
shadowsteel_elytra2 keysThe 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.
typeitemfile'shadowsteel_elytra.json'Furniture
runic_chest2 keystype: 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.
typeblockfile'runic_chest.json'crypt_tombstone2 keystypeblockfile'crypt_tombstone.json'steel_warden_model2 keysThe body of the Steel Warden (mobs.yml): worn by a display on the mob, not by a player.
typeitemfile'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
lang/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.
| File | Language | Texts |
|---|---|---|
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
prefix1common6command5confirm6help7pack21report34server6status13reload2address7log23license35