docs: rewrite skin creation workflow for current Lua backend
@@ -0,0 +1,78 @@
|
||||
<a id="top"></a>
|
||||
|
||||
# Tekton Dash Armageddon - Server Architecture
|
||||
|
||||
## Nakama Backend (`server/nakama/lua/`)
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
|
||||
The backend runs on Nakama, written in Lua. It provides authoritative server-side logic for the economy, anti-cheat, mail, and gacha pulls.
|
||||
|
||||
Godot clients call these endpoints via `BackendService.api_rpc_async()`.
|
||||
|
||||
### Key Lua Modules
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
- **`economy.lua`:**
|
||||
- `buy_currency`: Validates mock/Google/Apple receipts and directly adds Gold or subtracts Gold for Stars via `nk.wallet_update`.
|
||||
- `purchase_item`: Buys cosmetic items from the shop catalog. Validates wallet funds and writes to `inventory` storage.
|
||||
- **`gacha.lua`:**
|
||||
- `perform_gacha_pull`: Reads banner drop rates, rolls RNG on the server, manages pity counters, and deducts the pull cost from the wallet. Prevents client-side RNG manipulation.
|
||||
- **`inbox.lua`:**
|
||||
- `get_mail` / `claim_mail_reward`: Server-authoritative mail system. Rewards attached to mail are granted directly to the wallet on claim.
|
||||
- **`user.lua` & `admin.lua`:**
|
||||
- `admin_topup_gold`: Admin-only RPC to grant 999,999 gold.
|
||||
- `admin_ban_player`: Banning tool.
|
||||
- Profile and friend sync logic.
|
||||
|
||||
## Client / Server Wallet Synchronization
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
|
||||
There is **only one authoritative wallet** per user stored on the Nakama backend.
|
||||
|
||||
1. Lua calls `nk.wallet_update(changeset)` upon any transaction.
|
||||
2. The Godot client calls `UserProfileManager._reload_wallet()`.
|
||||
3. Godot fetches the new JSON string from `account.wallet`.
|
||||
4. `UserProfileManager` emits `profile_updated`.
|
||||
5. All connected UI components (Lobby Top Bar, Shop Panel, Gacha Panel) instantly update their text labels to reflect the unified `gold` and `star` balances.
|
||||
|
||||
## CI/CD and Hot-Patching Pipeline (`.gitea/workflows/`)
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
|
||||
The game features two distinct automated deployment pipelines.
|
||||
|
||||
### Full Releases (`ci.yml`)
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
- **Trigger:** Pushing a git tag (e.g. `v3.0.0`).
|
||||
- **Purpose:** Compiling heavy native binaries (Windows `.exe`, Linux binaries, macOS `.app`).
|
||||
- **Flow:**
|
||||
1. Clones repository.
|
||||
2. Pulls cached Godot 4.7 binary and export templates from `/cache/` on the runner to skip the 1.3GB download.
|
||||
3. Runs `godot --headless --export-release` for each platform.
|
||||
4. Zips outputs and uploads to Gitea's release asset API.
|
||||
- **Player Experience:** Must download and replace their entire game folder/executable.
|
||||
|
||||
### Hot-Patching (`deploy_patch.yml`)
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
- **Trigger:** Manual `workflow_dispatch` button from the Gitea UI.
|
||||
- **Purpose:** Pushing lightweight Godot asset changes (Scripts, Scenes, VFX, Sounds) instantly.
|
||||
- **Flow:**
|
||||
1. Python script `generate_version_json.py` consumes `CHANGELOG_DRAFT.md` and bumps `version.json`.
|
||||
2. Runs `godot --headless --export-pack` to build a single, shared `patch.pck` file.
|
||||
3. Force-pushes `patch.pck` and `version.json` to the orphan `patches` git branch.
|
||||
4. A `gitea-pages` Docker container serves this branch publicly at `https://raw.klud.top`.
|
||||
- **Player Experience:**
|
||||
1. Game boots to `boot_screen.tscn`.
|
||||
2. Fetches `https://raw.klud.top/danchie/tekton/version.json`.
|
||||
3. Downloads `patch.pck` if the version is newer.
|
||||
4. Mounts it into `user://` to override the base `res://` filesystem seamlessly.
|
||||
+242
-45
@@ -1,60 +1,257 @@
|
||||
# Skin Creation Workflow
|
||||
|
||||
How to author a new character skin and register it in the in-game shop.
|
||||
<a id="top"></a>
|
||||
|
||||
## 1. Create the Skin Material
|
||||
How to author a new character skin, register it in the game's shop/gacha catalog, and ship it to players.
|
||||
|
||||
Use the in-editor tool to build a color-maskable 3D material for a new character skin.
|
||||
---
|
||||
|
||||
- Open the **Skin Shader Generator**: `res://scenes/tools/skin_shader_generator.tscn`
|
||||
- Run the scene (F6).
|
||||
- Import your base albedo and mask textures.
|
||||
- Use the UI to visualize UV overlays and adjust color channels (Red, Green, Blue, Alpha masks).
|
||||
- Export the configured material as a `.tres` file into `assets/materials/skins/`.
|
||||
## Overview
|
||||
|
||||
## 2. Add the Skin to the Shop
|
||||
Each skin is defined in two places that must stay in sync:
|
||||
|
||||
Update the game's catalog. The editor tool rewrites both the local script and the server-side module in one click.
|
||||
| Layer | File | What it stores |
|
||||
|---|---|---|
|
||||
| **Client (visual)** | `scripts/managers/skin_manager.gd` | Mesh slots, material paths, override/overlay mode |
|
||||
| **Server (shop)** | `server/nakama/lua/economy.lua` | Item ID, name, category, price (gold/star) |
|
||||
|
||||
- Open the **Skin Catalog Editor**: `res://scenes/tools/skin_catalog_editor.tscn`
|
||||
- Press **F6** (or Right-click → Run Current Scene).
|
||||
- Click **"+ New Skin"** and fill in:
|
||||
- **ID**
|
||||
- **Name**
|
||||
- **Category**
|
||||
- **Price** (Gold / Stars)
|
||||
- The `.tres` material path exported in step 1.
|
||||
- Click **"💾 Save & Generate"**. This rewrites:
|
||||
- `res://scripts/managers/skin_manager.gd` (local catalog)
|
||||
- `res://server/nakama/tekton_admin.js` (server-side shop logic)
|
||||
The item `id` in `economy.lua` must match the dictionary key in `skin_manager.gd` `SKIN_CATALOG` exactly -- the Godot client looks up the item ID from the wallet/inventory and applies the matching skin data.
|
||||
|
||||
## Flow
|
||||
[Back to top](#top)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph phase1 [Skin Material Creation]
|
||||
A[Albedo & Mask Textures] --> B{Skin Shader Generator}
|
||||
B -->|Export| C[material.tres]
|
||||
end
|
||||
---
|
||||
|
||||
subgraph phase2 [Catalog Definition]
|
||||
C --> D{Skin Catalog Editor}
|
||||
D -->|Save & Generate| E[tekton_admin.js]
|
||||
D -->|Save & Generate| F[skin_manager.gd]
|
||||
end
|
||||
## Step 1: Create the Skin Material
|
||||
|
||||
subgraph phase3 [Shop Backend]
|
||||
E -->|SSH & nano| G[VPS: ~/tekton_admin.js]
|
||||
G -->|docker cp| H[Nakama Container]
|
||||
H -->|Restart| I[Live Shop Logic Sync]
|
||||
end
|
||||
Open the **Skin Shader Generator** at `res://scenes/tools/skin_shader_generator.tscn`.
|
||||
|
||||
subgraph phase4 [Asset Delivery]
|
||||
C --> J[Git Push]
|
||||
F --> J
|
||||
J -->|CI/CD| K[patch.pck]
|
||||
K -->|Auto Download| L[Player Client Assets]
|
||||
end
|
||||
1. Run the scene (F6).
|
||||
2. Import your base albedo and mask textures (PNG with color/alpha channels).
|
||||
3. Use the UI to visualize UV overlays and adjust color channels (Red, Green, Blue, Alpha).
|
||||
4. Export the configured material as a `.tres` file into `assets/materials/skins/` or a subfolder:
|
||||
- `assets/characters/skins/hat/`
|
||||
- `assets/characters/skins/clothing/`
|
||||
- `assets/characters/skins/gloves/`
|
||||
|
||||
**Material path conventions (Oldpop character):**
|
||||
|
||||
| Category | Example path |
|
||||
|---|---|
|
||||
| hat | `res://assets/characters/skins/hat/oldpop_mat_hat_blue.tres` |
|
||||
| costume/clothing | `res://assets/characters/skins/clothing/oldpop_mat_cloth_red_pant.tres` |
|
||||
| glove | `res://assets/characters/skins/gloves/oldpop_mat_gloves_blue.tres` |
|
||||
| accessory | `res://assets/characters/skins/accessory/` |
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Register the Skin in SkinManager (Client)
|
||||
|
||||
Open `res://scripts/managers/skin_manager.gd` and add a new entry inside `SKIN_CATALOG` (between `[BEGIN_SKIN_CATALOG]` and `[END_SKIN_CATALOG]` markers).
|
||||
|
||||
### Entry format
|
||||
|
||||
```gdscript
|
||||
"item_id": {
|
||||
"category": "head", # head | costume | glove | accessory
|
||||
"character": "Oldpop", # node name under CharacterRoot
|
||||
"slots": [
|
||||
{
|
||||
"mesh": "oldpop-hat1", # MeshInstance3D child name
|
||||
"mode": "override", # "override" | "overlay"
|
||||
"material": "res://path/to/material.tres"
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
After step 2, follow the [Nakama Deployment](./Nakama-Deployment) page to sync the backend, then the [Patch Release Workflow](./Patch-Release-Workflow) page to ship the assets.
|
||||
### Slot modes
|
||||
|
||||
- **`override`** -- `set_surface_override_material(0, mat)`. Replaces the base material entirely. Preserves the outline shader (`next_pass`) automatically.
|
||||
- **`overlay`** -- `material_overlay = mat`. Transparent layer on top of the base material. Good for costume/pant patterns.
|
||||
|
||||
### Multi-slot skins (costume example)
|
||||
|
||||
Costumes typically touch 3 meshes:
|
||||
|
||||
```gdscript
|
||||
"oldpop-grey-pant": {
|
||||
"category": "costume",
|
||||
"character": "Oldpop",
|
||||
"slots": [
|
||||
{ "mesh": "oldpop-body", "mode": "overlay", "material": "res://assets/characters/skins/clothing/oldpop_mat_cloth_grey_pant.tres" },
|
||||
{ "mesh": "oldpop-bottom1", "mode": "override", "material": "res://assets/characters/skins/clothing/oldpop_mat_cloth_grey_pant.tres" },
|
||||
{ "mesh": "oldpop-bottom2", "mode": "override", "material": "res://assets/characters/skins/clothing/oldpop_mat_cloth_grey_pant.tres" },
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
- Leave `"material"` as `""` if the `.tres` file is not ready yet. The slot is skipped gracefully.
|
||||
- Use the **Skin Catalog Editor** (`res://scenes/tools/skin_catalog_editor.tscn`) to avoid manual edits. Click **Save & Generate** to rewrite both `skin_manager.gd` and `economy.lua`.
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Register the Skin in Economy (Server)
|
||||
|
||||
Open `server/nakama/lua/economy.lua` and add a new entry to `SHOP_CATALOG_DEFS`.
|
||||
|
||||
### Catalog entry format
|
||||
|
||||
```lua
|
||||
{ id = "oldpop-blue-hat", name = "Oldpop Blue Hat", category = "head", gold = 100, star = 0, rarity = "Common", character = "Oldpop" },
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `id` | string | Must match the `SKIN_CATALOG` key in `skin_manager.gd` exactly |
|
||||
| `name` | string | Display name shown in shop |
|
||||
| `category` | string | `head` / `costume` / `glove` / `accessory` |
|
||||
| `gold` | number | Gold coin price (0 = not sold for gold) |
|
||||
| `star` | number | Star gem price (0 = not sold for stars) |
|
||||
| `rarity` | string | `"Common"` / `"Uncommon"` / `"Rare"` -- cosmetic label only |
|
||||
| `character` | string | Character this skin belongs to (e.g. `"Oldpop"`) |
|
||||
|
||||
### Existing catalog (12 items)
|
||||
|
||||
```
|
||||
oldpop-blue-hat head 100 gold Common Oldpop
|
||||
oldpop-green-hat head 100 gold Common Oldpop
|
||||
oldpop-red-hat head 100 gold Common Oldpop
|
||||
oldpop-yellow-hat head 100 gold Common Oldpop
|
||||
oldpop-og-pant costume 0 gold Common Oldpop (free)
|
||||
oldpop-grey-pant costume 150 gold Common Oldpop
|
||||
oldpop-red-pant costume 150 gold Common Oldpop
|
||||
oldpop-yellow-pant costume 150 gold Common Oldpop
|
||||
oldpop-blue-gloves glove 75 gold Common Oldpop
|
||||
oldpop-green-gloves glove 75 gold Common Oldpop
|
||||
oldpop-red-gloves glove 75 gold Common Oldpop
|
||||
oldpop-yellow-gloves glove 75 gold Common Oldpop
|
||||
```
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
---
|
||||
|
||||
## Step 4 (Optional): Add Skin as Gacha Prize
|
||||
|
||||
Gacha-only skins are registered in `server/nakama/lua/gacha.lua` inside `GACHA_DATA.real_prize_catalog`.
|
||||
|
||||
### Existing gacha skins (4 items)
|
||||
|
||||
```lua
|
||||
skin_gacha_rainbow_suit = { name = "Rainbow Suit", category = "costume", rarity = "real_prize", character = "" }
|
||||
skin_gacha_dragon_hat = { name = "Dragon Hat", category = "head", rarity = "real_prize", character = "" }
|
||||
skin_gacha_phantom_gloves = { name = "Phantom Gloves", category = "glove", rarity = "real_prize", character = "" }
|
||||
skin_gacha_neon_acc = { name = "Neon Accessory", category = "accessory", rarity = "real_prize", character = "" }
|
||||
```
|
||||
|
||||
Gacha skins also need a `skin_manager.gd` entry (same step 2 format) and a `skin_catalog_editor` entry so `SkinManager.apply_loadout()` can render them. The server catalog is optional -- gacha skins are not sold in the shop directly.
|
||||
|
||||
The gacha pulls these IDs from `GACHA_DATA.pools.real_prize`, so add your item_id there too.
|
||||
|
||||
```lua
|
||||
pools = {
|
||||
common = {"frag_common"},
|
||||
uncommon = {"frag_uncommon"},
|
||||
rare = {"frag_rare"},
|
||||
real_prize = {
|
||||
"skin_gacha_rainbow_suit",
|
||||
"skin_gacha_dragon_hat",
|
||||
"skin_gacha_phantom_gloves",
|
||||
"skin_gacha_neon_acc",
|
||||
-- add new skin here
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Deploy
|
||||
|
||||
### Hot patch (content only) -- recommended for skins
|
||||
|
||||
1. Commit changes to `experimental` branch:
|
||||
- `scripts/managers/skin_manager.gd`
|
||||
- `server/nakama/lua/economy.lua` (if shop item)
|
||||
- `server/nakama/lua/gacha.lua` (if gacha prize)
|
||||
- Material `.tres` files
|
||||
|
||||
2. Push to `experimental`.
|
||||
|
||||
3. Trigger `deploy_patch.yml` via Gitea UI workflow dispatch.
|
||||
- CI runs `--export-pack` to build `patch.pck`.
|
||||
- CI force-pushes `patch.pck` + `version.json` to `patches` branch.
|
||||
- Existing players auto-download on next boot via `GameUpdateManager`.
|
||||
|
||||
### Full binary release (if engine/templates changed)
|
||||
|
||||
Tag a version (e.g. `v2.5.0`) and push. CI builds all platform binaries and uploads to the release.
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
---
|
||||
|
||||
## Full Flow Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────┐
|
||||
│ 1. Create Material │
|
||||
│ skin_shader_generator │
|
||||
│ ────────────────── │
|
||||
│ Export .tres file │
|
||||
└────────┬─────────────┘
|
||||
v
|
||||
┌──────────────────────────────┐
|
||||
│ 2. Register in SkinManager │
|
||||
│ skin_manager.gd │
|
||||
│ ────────────────── │
|
||||
│ Add SKIN_CATALOG entry │
|
||||
│ (mesh slots + material) │
|
||||
└────────┬─────────────────────┘
|
||||
v
|
||||
┌──────────────────────────────┐
|
||||
│ 3. Register in Economy │
|
||||
│ economy.lua │
|
||||
│ ────────────────── │
|
||||
│ Add SHOP_CATALOG_DEFS │
|
||||
│ (price, name, category) │
|
||||
└────────┬─────────────────────┘
|
||||
v (optional)
|
||||
┌──────────────────────────────┐
|
||||
│ 4. Gacha Prize │
|
||||
│ gacha.lua │
|
||||
│ ────────────────── │
|
||||
│ real_prize_catalog + pools │
|
||||
└────────┬─────────────────────┘
|
||||
v
|
||||
┌──────────────────────┐
|
||||
│ 5. Git Push & CI │
|
||||
│ deploy_patch.yml │
|
||||
│ ────────────────── │
|
||||
│ patch.pck → players │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Cause | Fix |
|
||||
|---|---|---|
|
||||
| Skin visible in editor but not in-game | Material path wrong or `.tres` not exported | Verify `res://` path in SKIN_CATALOG, run `--export-pack` |
|
||||
| Skin purchase fails: "NotEnoughFunds" | Wallet balance insufficient | Check gold/star prices in economy.lua |
|
||||
| Skin visible on all characters | `character` field wrong | Set correct character node name |
|
||||
| Skin purchase fails: "ItemNotFound" | Item ID not in SHOP_CATALOG_DEFS | Add entry matching SKIN_CATALOG key |
|
||||
| Player downloads patch but skin missing | econmy.lua change didn't reach server | Nakama hot-reload: restart Nakama container or wait for next restart |
|
||||
| Outline shader lost on skin | next_pass not preserved | SkinManager preserves it automatically -- verify with latest `skin_manager.gd` |
|
||||
|
||||
[Back to top](#top)
|
||||
|
||||
Reference in New Issue
Block a user