Server Admin

Scuffed Cosmetics

Requirements, staff commands, permission nodes, config, content authoring, store delivery, content sync, storage and API.

Gameplay GuideView the players side of Scuffed Cosmetics

Last updated

Install, staff-command, permission, content and store-integration reference for server operators. For how the shop plays (tabs, profiles, wearables, skins, emotes, guild skies and boosts) see the Scuffed Cosmetics gameplay guide.

  • Plugin version: 2.39.0 (plugin name ScuffedCosmetics)
  • Engine range: >=0.7.0-pre.0 <0.8.0

Requirements

PluginVersionType
Hytale AssetModuleanyRequired
EndlessLevelingCoreanyOptional. Enables shared storage between servers, the grant outbox, the /ec menu entry and the prestige lookup for the Endless Origin award
Hytale NPC moduleanyOptional (declared in the manifest). Stand and Wander showcase stands are NPCs
EndlessLevelingOfficialServerPartneranyOptional (declared in the manifest)
Endless GuildsanyOptional. Guild land skies
ScuffedProtection1.1.43+Optional. Shows guild skies in the world
ScuffedProfessions0.25.71+Optional. Without it the Boosts tab is hidden, and boosts a player already owns cannot start

Without Endless Leveling every player record, price override and code lives in local files under mods/ScuffedCosmetics/. With an active Endless Leveling shared storage backend the same data is shared by every server on the network.

The plugin ships its own asset pack (IncludesAssetPack). It also writes up to three generated packs into mods/ (see below).


Installation and generated packs

Drop the jar into mods/ and start the server. On first start the plugin creates mods/ScuffedCosmetics/ and a default config.json.

Content you add by dropping files (costumes, player skins, particles) is packed into generated jars before the engine loads assets, so one restart loads new content:

Generated fileBuilt fromHolds
mods/SC_Costumes.jarmods/ScuffedCosmetics/costumes/ plus the sets built into the plugin jarCostumes, back items, tails, headwear
mods/SC_PlayerSkins.jarmods/ScuffedCosmetics/playerskins/Player skins
mods/SC_Particles.jarmods/ScuffedCosmetics/particles/Custom particle systems

Do not edit or delete these by hand while the server runs. Each is also rebuilt at shutdown so files dropped during a session load on the next boot.

An invalid model can make a whole generated pack fail to load, especially on servers started with asset validation. Test new models locally first. To recover, remove the bad folder and the matching generated jar, then restart.


Admin & Staff Commands

The player command is /cosmetic (aliases /cosmetics, /cos, /scos, /sc). Every admin command is /cosmetic admin <command>. They run from the console without any check (each console use is written to the log as an audit line) and from a player who is an operator or holds scuffedcosmetics.admin.

Where a command takes <player> it accepts an online name, a UUID, or a name the shared name index knows from any server. Where a token or cosmetic grant cannot find a player at all, it is parked under that name and delivered to the first player who logs in with it.

CommandDescription
/cosmetic admin grant <player> <id> [--txn <txn>]Give a cosmetic or profile. See list for ids
/cosmetic admin revoke <player> <id>Take a cosmetic or profile away. A cosmetic the player had equipped is unequipped live
/cosmetic admin tokens give <player> <amount> [--txn <txn>]Add Premium Tokens
/cosmetic admin tokens take <player> <amount>Subtract tokens. Refused when the player has fewer
/cosmetic admin tokens set <player> <amount>Set the balance
/cosmetic admin listPrint every cosmetic and profile with its id, price (or award-only) and unlock node
/cosmetic admin delete <id>Delete an admin-created cosmetic or designer profile. Built-ins are refused. Online wearers are unequipped
/cosmetic admin reloadconfigRe-read config.json and print the current store link
/cosmetic admin endlessorigin <preview|award|status|award-local>The one-time Endless Origin award (below)
/cosmetic admin showcase listList every showcase stand
/cosmetic admin showcase info <id>Every field of one stand, plus the items it cannot show
/cosmetic admin showcase poses [group]List pose groups, or the poses in one group
/cosmetic admin showcase set <id> <field> <value>Change one field. A value of - clears it
/cosmetic admin showcase outfit <id> <add|remove> <cosmeticId>Add or remove an item the stand wears
/cosmetic admin showcase tphere <id>Move the stand to where you stand, facing the way you face. Player only
/cosmetic admin showcase reloadRe-read showcases.json and respawn every stand
/cosmetic editorOpen the visual particle and weapon-effect editor. Operators and scuffedcosmetics.admin only
/cosmetic diagReport whether your equipped effects are still loaded and re-apply them. Operators and scuffedcosmetics.admin only

Every grant, revoke and token change is checked and written off the world thread. A change reports its result in the reply, and a failed storage write says FAILED so a store can retry.

The --txn argument

--txn is the replay-protection id for store deliveries. It must be 1 to 64 characters with no control characters and no { or }. A repeated id is refused as already delivered, so a re-fired webhook cannot double-credit.

A command without --txn, or with an unusable one, still delivers (it gets a one-off id) but is not protected against replays. A queued delivery says so in its reply, and an unusable id logs an audit warning. See Store integration below.

Endless Origin award

endlessorigin hands the Endless Origin profile once to every player whose best Endless Leveling profile is at prestige 20 or higher, online or not.

ActionEffect
previewLists who qualifies. Grants nothing
awardRuns the award. It claims a marker first so no other admin or server can run it again, freezes the recipient list, and delivers through the grant outbox
statusShows whether the award has run, who ran it and the counts
award-localSame as award on a standalone server with no shared storage, where the marker is only local

If a run is interrupted, run award again: it finishes the same frozen list and nobody is paid twice. After it closes, the profile is only available through /cosmetic admin grant <player> profile_endless_origin. The award needs Endless Leveling for the prestige lookup.


Permissions

NodeEffect
scuffedcosmetics.adminAdmin tools: the Admin View switch, the Admin and Editor tabs, /cosmetic admin ..., /cosmetic editor, /cosmetic diag. Operators have it implicitly
scuffedcosmetics.unlock.<id>The player can equip that cosmetic or profile without owning it. Find ids with /cosmetic admin list
scuffedcosmetics.unlock.*Unlocks every cosmetic and profile
scuffedcosmetics.emote.bypassPlays every emote without owning it. Operators have it implicitly
  • Unlocks are live. The item is never added to the player's saved collection, so removing the node removes the item, and a player who no longer qualifies has it unequipped at their next login. Weathers are the exception: an unlock node does not count as owning one.
  • The checks read nodes from the permission provider as exact strings. Grant scuffedcosmetics.unlock.* and scuffedcosmetics.admin themselves, not a broader wildcard.
  • /cosmetic and /emote are open to the default hytale:Adventurer group so every player can use them. The admin subcommands do their own check on top.
  • The plugin registers its own /emote, which replaces the base one. It behaves the same for emotes the player may play, and for the rest it points the player to the shop.

Admin View and the in-menu tools

Operators and scuffedcosmetics.admin holders see an Admin View switch in the menu. It starts off, so staff see the shop as a player sees it. Turning it on (it is remembered until the server restarts) adds the Admin and Editor tabs and admin controls on every card.

Controls on a selected item:

ControlEffect
Grant to MeGives the item to you
For SaleToggles between purchasable and award-only
HideTakes the item off every non-admin screen, owners included
RetireTakes it out of the shop. Players who own it keep seeing and using it
RemoveRemoves it for good, after a confirmation (below)
RenameSets a display name, up to 32 characters
PriceSets the token price
Select multipleApplies Show, Hide, For sale, Award only or a price to many items. First / Earlier / Later / Last reorder items inside their section

The Admin tab also holds, for an online player, token Give / Take / Set and a grant and revoke list; a paged Purchase history; Giveaway Codes; the Retired From Shop and Removed For Good lists with Return to shop and Restore buttons; and the Showcase Stands manager.

These overrides are saved in admin_overrides.json and, with shared storage, apply on every server. They cover price, sale state, hidden, retired, the name, the order and removal.

Remove for good unloads the item from the next boot: the generated packs are built without it and nothing can list, buy, grant or equip it. The files stay on disk, and Restore reverses it at the following boot. A removal is refused when:

  • any non-admin player owns the item or holds its unlock node;
  • a store purchase of it is still waiting to be delivered;
  • another item is built from it;
  • other code depends on it (costume_demonlord_wings_fast, used by the Endless Blade during Massacre, and playerskin_default).

The owners are read again shortly after, and the removal is undone if someone bought the item on another server in the meantime.


Configuration

mods/ScuffedCosmetics/config.json is written with defaults on first start. Unknown keys are kept when the plugin rewrites it. Run /cosmetic admin reloadconfig after editing.

{
  "shopUrl": "https://store.example.com",
  "grantSweepSeconds": 60,
  "grantRetentionDays": 14,
  "contentSync": "off",
  "endlessOriginLength": "medium"
}
KeyDefaultEffect
shopUrlA link to the Endless Leveling storeWhere the Get Tokens button sends players. It must start with http:// or https://. Set your own store link, or leave it blank to hide the button
grantSweepSeconds60How often the grant outbox is checked for deliveries owed to players on this server. Range 15 to 600. Read at startup
grantRetentionDays14How long a delivered grant row is kept before it is pruned. Range 1 to 365
contentSyncoffsource, mirror or off. See Content sync
endlessOriginLengthmediumHow far the Endless Origin ribbons stream: short, medium or long

A numeric value that is out of range is clamped. An unknown contentSync or endlessOriginLength value is logged and treated as off and medium. A config file that cannot be read keeps the settings already in memory.

Other files under mods/ScuffedCosmetics/

PathPurpose
players/<uuid>.jsonOne record per player: tokens, owned items, equipped items, history
admin_overrides.jsonPer-item hidden, sale, price, name, order, retired and removed state
custom_cosmetics/<id>.jsonCosmetics created in the editor
profiles/<id>.jsonSaved designer profiles, loaded at startup
giveaway_codes.jsonGiveaway codes
emotes.json, weathers.json, playerskins.jsonOptional shop overrides, described below
showcases.json, showcase_stats.jsonShowcase stands and their interaction counts
guild_weathers.jsonThe sky each guild chose per chunk on this server
txn_seen.txtStore transaction ids delivered while shared storage was unavailable
server-id.txtThis server's generated identity for shared storage
awards/endless-origin.jsonLocal Endless Origin award marker on a standalone server
unsynced/<time>/Files a content-sync mirror moved aside, kept 30 days

Adding content

Everything here is data. No code is needed. All prices are in Premium Tokens and can be changed later from the menu or with the override files.

Costumes, back items, tails and headwear

Put models in mods/ScuffedCosmetics/costumes/, then restart.

  1. Author a chest-armor-style .blockymodel. The rig chain is Pelvis > Belly > Chest with your geometry as child nodes of Chest. The rig bones should be pieces (isPiece); the packer repairs known bone names, but authoring them correctly is safer.
  2. Drop <Name>.blockymodel and <Name>.png (the texture) into the folder. Add <Name>.icon.png for the shop icon and <Name>.json for the settings.
  3. Restart. The costume appears under Costumes with the id costume_<name>, where the name is lowercased and runs of other characters become _.

A model with no matching .png (or <Name>_<Variant>.png) is skipped with a log line. Two files whose names reduce to the same id clash, and the later one is skipped.

Slots. One item per slot can be worn at once. The slot is set with slot in the sidecar, and each non-costume slot needs a particular anchor bone:

slotSlotRequired anchor bone (as an isPiece node)
absent, costume or fullFull costumeNone
backBack itemBack-Attachment or Chest
tailTailPelvis
headHeadwearHead

An unknown or non-string slot, or a missing anchor bone, rejects the drop with a log message naming the folder. Back, tail and head items must keep decoupled at true.

Sidecar <Name>.json, all keys optional:

KeyDefaultEffect
nameThe file name with _ as spacesDisplay name
descriptionCustom costume.Card text
price800Token price
forSaletruefalse makes the item award-only
rarityRARECOMMON, RARE or LEGENDARY
slotcostumeSee above
decoupledtruefalse puts a full costume on the legacy armor-visual channel: it replaces the look of the chest armor and animations are ignored
backFittruefalse turns off the automatic back fit
animationsnonePer-state animation files, see below
animationSpeed1.0Multiplies the playback speed of every overridden animation. Must be above 0
variantsnoneNames and overrides for colour variants
allowLargerTexturesfalseLets a dropped copy use a bigger texture than the built-in copy of the same costume

A # in a name or description is removed.

{
  "name": "Phoenix Wings",
  "description": "Wings of living flame.",
  "slot": "back",
  "price": 1200,
  "forSale": true,
  "rarity": "LEGENDARY",
  "variants": {
    "Red": "Crimson Wings",
    "Gold": { "name": "Gilded Wings", "price": 2000 }
  }
}

Colour variants. Extra textures named <Name>_<Variant>.png become separate shop items sharing the one model. Add <Name>_<Variant>.icon.png for their icons. A variant entry is a plain string (its name) or an object with name, description, price, forSale and rarity. If the main item is award-only, its variants stay award-only unless the variant sets forSale to true.

Animated wings. Give the model extra bones under a rig bone, author replacement player animations that also move them, drop the .blockyanim files next to the model and map them by Hytale animation state:

"animations": {
  "Idle": "Idle_Wings.blockyanim",
  "Fall": { "files": "Fall_Wings.blockyanim", "blend": 0.4 },
  "JumpSprint": [ "Jump_Far_Wings.blockyanim", "Jump_Far2_Wings.blockyanim" ]
},
"animationSpeed": 1.5

A single file applies to every entry of a state, a list maps entry by entry, blend sets that state's blending time and a per-state speed overrides animationSpeed. The plugin bakes a player model per animated costume, and only the wearer is switched onto it.

Folders. A costume can live in its own subfolder, which keeps animated sets tidy. Names must stay unique across all folders. A file named in animations is looked for in the costume's folder, then in _shared/, then at the top level. Folders starting with _ or . are not scanned as costumes. Moving a costume into a folder changes nothing for players.

Built-in sets. The plugin jar carries the wing sets Celestial, Demonlord, Feathered, Fire, Justicar, Primordial Aatrox and the Clockwork Aegis and Servo Array pair, and six Pride Capes. Nothing needs to be dropped for them. To retune one, copy all of its files into the drop folder under the same names and edit them: a complete copy replaces the built-in one. A copy missing any file the built-in one has (the usual result of an interrupted upload) is ignored with an INCOMPLETE warning, and the built-in copy is used. A copy with a larger texture is ignored unless allowLargerTextures is set, because oversized costume textures can crash clients when an entity spawns.

By default the Celestial, Primordial Aatrox, Clockwork Aegis and Servo Array wings are on sale and the Demonlord, Feathered, Fire and Justicar wings are award-only. The six Pride Capes are on sale. Change any of this with the override controls.

Only ship models, textures and animations you made or have permission to distribute. Do not copy Hytale game assets into a costume.

Player skins

Full-body model swaps. Make one subfolder per skin in mods/ScuffedCosmetics/playerskins/<Name>/ and add the body-part models and textures, authored against the vanilla player rig:

playerskins/Samurai/
  Head.blockymodel   Head.png
  Chest.blockymodel  Chest.png        (or Chest_Texture.png)
  Hands.blockymodel  Hands.png
  Legs.blockymodel   Legs.png
  Cape.blockymodel   Cape.png         (optional)

The base body, animations and face come from the game's own player model. Restart once. All skins are packed into mods/SC_PlayerSkins.jar, appear under Skins with the id playerskin_<name>, and a free Default (Your Skin) entry is always added.

Optional skin.json in the skin folder:

KeyDefaultEffect
nameThe folder name, spacedDisplay name
price1500Token price
iconIngredient_Motes_LightItem id used as the card icon
scaleAsset defaultModel scale
keepFacetrueKeep the vanilla face attachments
injectHeadAutomaticKeep the wearer's own head and hair. On by default when the skin ships no head part
keepAccessoriestrueKeep the wearer's headband, glasses, earrings and cape
keepFaceAccessoryfalseKeep the wearer's face accessory on a skin that ships its own head
shipsCapeAutomaticThe skin has its own back piece, so the wearer's cape is left off. On by default when a part is named Cape, Cloak, Mantle, Wings or Backpack
partsAutomaticAttach order of the part names

playerskins.json overrides name, icon, price, scale, keepAccessories, shipsCape and keepFaceAccessory per skin, keyed by the model id SCSkin_<Name>. Hand-built model assets whose id starts with SCSkin_ are also discovered.

Particles

Custom particle systems are authored in an external tool that exports a particle system and spawner pair, then dropped in:

  • the exported folder to mods/ScuffedCosmetics/particles/<Name>/, or
  • the exported .zip to mods/ScuffedCosmetics/particles/<Name>.zip (extracted on boot and renamed .zip.imported).

Restart. A dropped particle is a building block, not a shop item: open /cosmetic editor (Particles mode), pick it, choose follow or trail and a bone, place it, set a name and price, and save. The editor writes a profile.json into that particle's folder under particles/ and registers the profile straight away. Bone-glued (follow) effects use temporary previews until the next restart packs them.

To publish a single particle without the editor, add a profile.json beside the export:

{
  "name": "Fire Aura",
  "description": "A ring of flame about you.",
  "publish": true,
  "price": 1200,
  "forSale": true,
  "rarity": "LEGENDARY",
  "mode": "follow",
  "system": "FireAura",
  "bone": "Chest",
  "offset": { "x": 0, "y": 0.2, "z": -0.1 },
  "scale": 1.0,
  "cadence": 0.4
}

The profile id is particle_<folder name>; defaults are price 800 and rarity RARE. mode is follow (glued to a bone and turning with the model) or trail (a wake left behind). It defaults to follow for trigger CONTINUOUS and to trail for one-shot triggers (ON_JUMP, ON_LAND, ON_HEAVY_LAND, ON_DEATH, ON_STRIKE, ON_GLIDE, ON_ROLL, ON_SPLASH, ON_EQUIP). cadence applies to trails. bone applies to follow mode (Origin, Pelvis, Belly, Chest, Head, Back-Attachment, left and right shoulder and hand). The spawner must be connected to the system in the export or nothing renders.

An invalid particle folder is skipped with a log line naming the file. It cannot stop the server.

Built-in profiles: Ember Sovereign, Frostbound Spirit, Aurora Veil and Pride Trail are on sale at 1100, 1275, 500 and 800 tokens by default. Endless Origin is award-only.

Emotes

Only emotes that mods or asset packs add are sold. The emotes built into Hytale are never listed and stay free for everyone. Each added emote becomes the item emote_<id> at 250 tokens, COMMON. Optional emotes.json, keyed by the engine emote id:

{
  "Wave": { "name": "Wave", "price": 100, "icon": "Ingredient_Motes_Light" },
  "Dance": { "price": 0 }
}

An emote that is for sale at price 0 is free for every player. Hide, sale and price overrides from the menu apply on top.

Weathers

Weathers a mod or pack adds are listed as guild skies. Weathers that come with the base game are never sold. Every weather starts off sale; put one on sale from the menu (the price defaults to 500 tokens), or in weathers.json, keyed by the engine weather id:

{
  "Blood_Moon_Custom": { "name": "Blood Moon", "description": "A red sky.", "price": 500, "icon": "Plant_Petals_Blood" }
}

A price there puts the weather on sale from boot.


Showcase stands

A showcase is a figure that models cosmetics and opens the shop on a press of the interact key. Create and edit stands from Showcase Stands in the Admin tab, or with the commands above. The setup presets are Wing Display, Hero Mannequin, Greeter, Wandering Model, Dancer, Seated and Blank.

Field (showcase set)ValuesMeaning
enabledtrue or falseShow or hide the stand
modeSTATUE, STAND, WANDERStatue is a bare figure and the only one that can spin. Stand and Wander are invulnerable NPCs. Wander roams around its spot
spintrue or falseTurn on the spot. Statue only
spinSeconds1 to 120Seconds per revolution. Default 8
holdSeconds1 to 600Seconds each item in the cycle is shown. Default 8
yawradiansFacing when not spinning
posepose id, or -A pose from the pose library. Use showcase poses to list them
poseModelayer or idleLeave on layer unless a pose fails to show for players who arrive late
featuredcosmetic idThe item the interact key opens the shop on
nameplatetrue or falseShow a name above the figure
labeltextThe nameplate text. Defaults to the featured item's name
mainHand, offHanditem idHeld items. Stand and Wander only
armorcomma list, up to 4Armor shown. Stand and Wander only
lookAtPlayerstrue or falseTurn the head toward players within 10 blocks. Stand and Wander only
wanderRangeSMALL, MEDIUM, LARGE4, 8 or 16 blocks from home

A stand has a cycle (one costume or one emote at a time) and an outfit of cosmetics worn all the time. Items that cannot be shown are skipped silently; showcase info lists them.

Stands only spawn while a player is within 64 blocks. A wanderer more than 2.5 ranges from home, or 8 blocks below it, is removed and returns at home. Stand roles carry NPC in their id, so Endless Leveling mob leveling skips them. Interaction counts per stand are saved at most once a minute to showcase_stats.json.


Store integration

The shop sells nothing for money inside the game. Players buy Premium Tokens from your web store, and the store credits them by running a console command. Any store that can run a console command after a purchase works. Never put keys, secrets or your store URL in the command.

Set the store link in config.json (shopUrl) so the Get Tokens button points players to it.

The package command

Add this as the package's command action, run on purchase:

cosmetic admin tokens give <player> <amount> --txn <transaction id>

Use your store's placeholders for the player and the transaction id. The player can be a name or a UUID; a UUID is safer because a name change cannot misroute it. For example, a package worth 500 tokens:

cosmetic admin tokens give {uuid} 500 --txn {transaction}

Cosmetics can be sold directly with cosmetic admin grant <player> <id> --txn <transaction id> the same way.

Set the package so it does not require the buyer to be online when the command runs. The delivery does not need the buyer present on this server or on any server.

Always pass --txn. The transaction id becomes the key of a delivery row in the shared grant outbox. Inserting that row is atomic across servers, so a retried webhook that reaches another node cannot credit twice. Without it a re-fired command credits again.

  • If the id still contains a literal { or } (the store did not substitute the placeholder), it is refused as a replay key, the purchase is delivered anyway, and the log gets an UNUSABLE --txn id audit warning. Test one purchase and check for that line.
  • If the buyer is on this server, the delivery is applied at once. If they are on another server or offline, it is queued and delivered at their next login anywhere, or within one sweep interval if they are online elsewhere.
  • If the name matches no known player, it is parked and binds to the first player who logs in with that name. A typo in a hand-typed name can hand a grant to someone else, so use UUIDs for manual grants.
  • Deliveries and the name index use the shared tables scuffedcosmetics.grants and scuffedcosmetics.names when shared storage is active.
  • If shared storage is unavailable, delivery falls back to this server only, protected by txn_seen.txt. Replays on other servers are not caught in that state, and each such delivery logs an audit warning.

Token balances are capped at the integer limit: a grant that would overflow a balance is refused with a FAILED reply instead of being clamped.

Giveaway codes

Staff create codes in Admin, Giveaway Codes. A code pays tokens, or one cosmetic or profile chosen from a list.

SettingValue
FormatTwelve characters from ABCDEFGHJKMNPQRSTUVWXYZ23456789, shown as XXXX-XXXX-XXXX
LabelUp to 40 characters, shown in history (a streamer or event name)
Expiry1 to 365 days, default 30
Redeem limitOne redemption per code, network-wide
LockoutA player with 8 wrong codes inside 10 minutes cannot redeem until the window ends

Codes can be revoked from the list. A code for an item the player already owns is refused without being used. A created code is shown once in the panel and written to the server log, so copy it then.

Global Boosts

The Boosts tab sells two products and stores what players buy in their record.

Product idStartsDefault price
global_boost_allThe all_60 Scuffed Professions boost template on every profession590 tokens
global_boost_professionThe profession_60 template on one profession chosen at use390 tokens

Price and sale state go through the same override layer as any item. Using a boost asks ScuffedProfessions to start it under a one-off transaction id, so it is started at most once on any server. Without ScuffedProfessions (or with a version below 0.25.71) the Boosts tab is hidden and boosts a player already owns cannot be used; they stay in stock.


Content sync

On a network, one server can be the source of the costume, skin and particle folders so content is added once.

  • Set contentSync to source on exactly one server and mirror on the others. It needs shared storage.
  • The source publishes costumes/, playerskins/, particles/ and playerskins.json, and only in the state it booted with, so a model the engine rejects stops at the source. Mirrors copy the content, and it loads at a mirror's next restart.
  • A pass runs every 5 minutes. Files are downloaded to a staging folder and checked by hash before any live file changes. Nothing is deleted: replaced files and files the source does not have go to unsynced/<time>/ and are kept 30 days.
  • Folders changed in the last two minutes are not published, large removals wait 30 minutes, and a file that does not parse as JSON is held back.
  • A second server set to source while the first holds a live lease does not publish.

Leave it off on a single-server install.


Data & storage

DataWhere
Player recordplayers/<uuid>.json, plus the scuffedcosmetics.players shared table when available
Price and shop overridesadmin_overrides.json, plus the scuffedcosmetics.overrides table
Giveaway codesgiveaway_codes.json, plus the scuffedcosmetics.codes table
Grant outbox and name indexThe scuffedcosmetics.grants and scuffedcosmetics.names tables
Endless Origin award markerThe scuffedcosmetics.awards table, or awards/endless-origin.json standalone
Content syncThe scuffedcosmetics.content table
  • A player record holds the token balance, owned items, equipped items, owned profiles and the equipped profile, the last 400 history entries, delivered transaction ids and boost stock. Every token change carries a source such as a purchase, a code or an admin name.
  • Files are written atomically (a temporary file, then a move). If a player file exists but cannot be read, the plugin refuses to write over it and refuses grants and purchases for that player until it can be read, so a disk error cannot reset a balance.
  • Fields written by a newer version of the plugin are kept when an older one rewrites a record. Update every server in a network together all the same.
  • A record is loaded when the player connects, saved after every change and on disconnect, and then dropped from memory.

Integrations

PluginTypeUse
EndlessLeveling CoreOptionalShared storage across servers, the grant outbox, the Endless Link /ec menu entry (page key cosmetics), the prestige lookup for the Endless Origin award, and exemption of showcase NPCs from mob leveling
Endless GuildsOptionalGuild membership, officer ranks and claimed chunks for guild land skies
ScuffedProtectionOptionalForces the chosen sky over guild land and plays sky previews
ScuffedProfessionsOptionalStarts and reports Global Boosts
Hytale NPC moduleOptionalStand and Wander showcase bodies

Every one of these is reached through reflection and resolved lazily, so the shop still opens when one is missing. A missing or too-old ScuffedProtection or ScuffedProfessions logs a single warning.


API for other mods

Other plugins can get the service with ScuffedCosmeticsApi.get() (package com.zbeve.scuffedcosmetics.api). It returns null before the plugin finishes setup or after shutdown. All grant and spend calls take a source string that is written to the player's history, 1 to 128 characters with no control characters.

MethodDescription
getTokens(uuid)Token balance
grantTokens(uuid, amount, source)Add tokens. Throws if the balance would overflow or the player's data is unreadable
spendTokens(uuid, amount, source)Subtract tokens. Returns false when short
ownsCosmetic(uuid, id) · isUnlocked(uuid, id)Owned, or owned or unlocked by permission
grantCosmetic(uuid, id, source) · revokeCosmetic(uuid, id)Grant or revoke an item
getOwnedCosmetics(uuid)The owned ids
equip(uuid, id) · unequip(uuid, category) · unequipAll(uuid)Equip state, including the live visual
getEquipped(uuid, category)The equipped id for a category
peekEquipped, peekEquippedProfile, isDataLoaded, requestLoadCache-only reads that never touch storage. Use these on a world thread
buyCosmetic(uuid, id, source)Atomic purchase that applies the override price and sale state
getCatalog() · getCosmetic(id)Read the catalog
getProfiles() · getProfile(id)Read profiles
ownsProfile · getOwnedProfiles · getEquippedProfileProfile ownership and equip state
buyProfile · grantProfile · revokeProfile · equipProfile · unequipProfileProfile actions

Categories are TRAIL, AURA, WINGS, HEAD_ACCESSORY, FACE_ACCESSORY, EAR_ACCESSORY, GLOVES, SKIN_FEATURE, WEAPON_FX, WEAPON_SKIN, COSTUME, BACK_ITEM, TAIL, HEAD_ITEM, PLAYER_MODEL, EMOTE and WEATHER. The menu shows only the modeled wearable, skin, emote, weather and profile categories; the others exist for stored data and other plugins.


Troubleshooting

SymptomCheck
A dropped costume does not appearRestart once. Look for skipped, rejected, INCOMPLETE or unreadable lines naming the file. A missing texture, a missing anchor bone or a duplicate name are the usual causes
A purchase did not creditSearch the log for [SC][AUDIT]. UNUSABLE --txn id means the store did not fill the transaction placeholder. FAILED means storage could not be read or written, and the reply says to retry
Tokens credited twiceThe command ran without --txn, or the store sent a different id each time
Equipped effects stop rendering after a reloadRun /cosmetic diag. It reports whether the effect assets are still loaded and re-applies them
A weather is bought but not visibleIt needs Endless Guilds, ScuffedProtection 1.1.43 or newer, an officer who applied it to a chunk of claimed land, and no forced region or world weather
A Global Boost will not startCheck that ScuffedProfessions 0.25.71 or newer is installed and its boosts are running. The boost stays pending and starts when it answers
Players see a stale pricePrices come from admin_overrides.json, shared across servers when storage is shared. Check that every server has the same shared storage