This skill should be used when building cube draft configurations for GEMP-SWCCG from CSV files containing card names. It handles mapping card names to IDs, generating cube configurations with add-on card support, and integrating the cube into the application (swccgDrafts.json registration and frontend dropdown setup).
Build cube draft configurations for GEMP-SWCCG by mapping CSV files of card names to card IDs and generating properly structured cube configuration JSON files. This skill covers the complete workflow from CSV preparation through deployment, including the critical playtesting card exclusion requirement and add-on card pack structure.
Use this skill when:
CRITICAL REQUIREMENT: Unless explicitly told otherwise, you MUST use side-specific databases with defensive shields filtered out when mapping light/dark side cards. This prevents:
See Step 3 for detailed instructions on creating filtered databases.
Create three CSV files in the cube source directory:
CSV Column Structure:
["card1_id", "card2_id"][["primary_id", "addon1_id"], ["card2_id"]]
Example multi-column CSV:
Admiral Ackbar,Home One
Obi-Wan Kenobi,Kenobi's Lightsaber
Luke Skywalker,,
Produces:
[
["admiral_ackbar_id", "home_one_id"],
["obi_wan_id", "lightsaber_id"],
["luke_id"]
]
Directory Structure:
src/gemp-swccg-server/src/main/resources/draft/cube_building/
└── <cube_name>/
├── default_cards.csv
├── cube_worlds_lightside.csv
└── cube_worlds_darkside.csv
The card mapping process requires an up-to-date card database that EXCLUDES playtesting cards (set 501).
./bin/gemp export-cards src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
Why this matters: The CardDataExporter automatically excludes set501 cards via a filter. If this step is skipped or uses old data, playtesting cards will appear in the cube.
Verification:
grep -c '"cardId": "501_' src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
# Must output: 0
IMPORTANT: Unless explicitly told otherwise, you MUST filter out defensive shields from the side-specific databases to prevent slug collisions. Cards like "Battle Plan" exist as both Effect and Defensive Shield with the same slug - without filtering, the defensive shield version will be incorrectly mapped instead of the effect.
Create filtered databases excluding defensive shields:
bash <<'BASH'
cd src/gemp-swccg-cards/src/main/resources
# Create light side database without defensive shields
jq '[.[] | select(.side == "LIGHT" and .cardCategory != "DEFENSIVE_SHIELD")]' \
card_blueprint_database.json > /tmp/card_blueprint_database_light_no_shields.json
# Create dark side database without defensive shields
jq '[.[] | select(.side == "DARK" and .cardCategory != "DEFENSIVE_SHIELD")]' \
card_blueprint_database.json > /tmp/card_blueprint_database_dark_no_shields.json
BASH
Verify filtering worked:
bash <<'BASH'
echo "Light side defensive shields (should be 0):"
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' \
/tmp/card_blueprint_database_light_no_shields.json
echo "Dark side defensive shields (should be 0):"
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' \
/tmp/card_blueprint_database_dark_no_shields.json
BASH
Why this matters:
Map each CSV file to card IDs using the map_card_names_to_ids script with side-specific, defensive-shield-filtered databases:
CUBE_DIR="src/gemp-swccg-server/src/main/resources/draft/cube_building/<cube_name>"
# Map default/core cards (use main database)
./bin/map_card_names_to_ids \
$CUBE_DIR/default_cards.csv \
$CUBE_DIR/default_cards_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
# Map light side packs (use light-only, no shields)
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_lightside.csv \
$CUBE_DIR/cube_worlds_lightside_mapped.json \
/tmp/card_blueprint_database_light_no_shields.json
# Map dark side packs (use dark-only, no shields)
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_darkside.csv \
$CUBE_DIR/cube_worlds_darkside_mapped.json \
/tmp/card_blueprint_database_dark_no_shields.json
Output Files:
*_mapped.json - Card ID arrays (flat or nested based on CSV structure)*_mapped_log.json - Processing logs with missing cards and warningsReview the logs for:
Script Behavior:
-)-v suffix (virtual fallback)Use create_cube to build the final configuration:
python3 bin/create_cube \
src/gemp-swccg-server/src/main/resources/draft/cube_building/<cube_name> \
src/gemp-swccg-server/src/main/resources/draft/<outputFile>.json
Required Files in Cube Directory:
default_cards_mapped.jsoncube_worlds_lightside_mapped.jsoncube_worlds_darkside_mapped.jsonConfiguration Parameters (hardcoded in script):
count: 9 - Pack choices shown per roundrepeat: 56 - Number of draft roundsobjChoiceCountPerSide: 0 - No objective picks (standard cube)Output Structure:
{
"format": "cube",
"fixedCount": 0,
"objChoiceCountPerSide": 0,
"choiceCountPerSide": 56,
"lightObjCards": [],
"darkObjCards": [],
"startingPool": {
"type": "cubeDraftRun",
"data": {
"coreCards": ["5_164", "12_164", ...]
}
},
"choices": [
{
"type": "cubePackObjPick",
"repeat": 56,
"data": {
"count": 9,
"packs": [
["110_6", "107_5"],
["213_31"],
...
]
}
},
{ /* dark side choice with same structure */ }
]
}
Add the cube to the draft registry:
File: src/gemp-swccg-server/src/main/resources/swccgDrafts.json
[
{
"type": "c7_draft",
"location": "/draft/cube7Draft.json"
},
{
"type": "<cube_identifier>_draft",
"location": "/draft/<outputFile>.json"
}
]
Important:
type - Format identifier (used in code and database)location - Path relative to resources directoryAdd the cube to the league admin dropdown:
File: src/gemp-swccg-async/src/main/web/includes/admin/leagueAdmin.html
Location: Around line 459 in the solo draft form
<select name="format">
<option value="c7_draft">Cube v7 Draft</option>
<option value="c7obj_draft">Cube v7 Draft + Obj</option>
<option value="<cube_identifier>_draft">Your Cube Name</option>
</select>
Critical: The value attribute MUST match the type field in swccgDrafts.json
# Rebuild server module (contains cube config)
./bin/gemp reload-fast
# Verify in browser
# 1. Access http://localhost:17001/gemp-swccg/
# 2. Login as admin
# 3. Navigate to League Admin → Add Solo Draft League
# 4. Verify cube appears in format dropdown
# 5. Create test league and access draft to confirm no errors
Before finalizing:
Root Cause: Old card database contains set 501 cards
Solution:
grep '"501_' <cube_config>.jsonSolution: Run card export command from Step 2
Cause: Card name matches multiple versions/sets
Solution: Script auto-selects first match. Review *_mapped_log.json to verify correct card. If wrong, update CSV with more specific name or set identifier.
Cause: Card doesn't exist or is misspelled
Solution:
Cause: Registration mismatch or cache issue
Solution:
type in swccgDrafts.json matches value in leagueAdmin.html./bin/gemp reload-fastSymptom: java.lang.ArithmeticException: / by zero at DefaultSoloDraft.java:66
Cause: Missing null check for objChoiceCountPerSide
Solution: Verify line 66 has:
if (_objChoiceCountPerSide > 0 && offset % _objChoiceCountPerSide == 0) {
If missing > 0 check, apply fix and rebuild.
gemp-swccg/
├── bin/
│ ├── gemp # CLI helper
│ ├── map_card_names_to_ids # CSV → IDs mapper
│ └── create_cube # Cube config generator
│
├── src/gemp-swccg-cards/src/main/resources/
│ └── card_blueprint_database.json # Card database (no set501)
│
├── src/gemp-swccg-server/src/main/resources/
│ ├── draft/
│ │ ├── <outputFile>.json # Your cube config
│ │ └── cube_building/
│ │ └── <cube_name>/ # Source files
│ │ ├── default_cards.csv
│ │ ├── default_cards_mapped.json
│ │ ├── cube_worlds_lightside.csv
│ │ ├── cube_worlds_lightside_mapped.json
│ │ ├── cube_worlds_darkside.csv
│ │ └── cube_worlds_darkside_mapped.json
│ │
│ └── swccgDrafts.json # Draft registry
│
├── src/gemp-swccg-server/src/main/java/.../draft2/
│ └── DefaultSoloDraft.java # Draft logic (line 66 fix)
│
└── src/gemp-swccg-async/src/main/web/includes/admin/
└── leagueAdmin.html # Frontend admin UI
Single card pack:
["card_id"]
Pack with add-ons:
["primary_card_id", "addon1_id", "addon2_id"]
Player receives ALL cards when picking the pack.
coreCards from startingPoolchoiceCountPerSide)count: 9)cubePackObjPick - Pack-based (supports add-ons)cubePick - Single card (no add-ons)Always use cubePackObjPick for consistency, even without objectives.
# 1. Create directory
mkdir -p src/gemp-swccg-server/src/main/resources/draft/cube_building/my_cube
# 2. Add CSV files (user provides)
# - default_cards.csv
# - cube_worlds_lightside.csv
# - cube_worlds_darkside.csv
# 3. Update card database
./bin/gemp export-cards src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
# 4. Create side-specific databases WITHOUT defensive shields
bash <<'BASH'
cd src/gemp-swccg-cards/src/main/resources
# Light side without defensive shields
jq '[.[] | select(.side == "LIGHT" and .cardCategory != "DEFENSIVE_SHIELD")]' \
card_blueprint_database.json > /tmp/card_blueprint_database_light_no_shields.json
# Dark side without defensive shields
jq '[.[] | select(.side == "DARK" and .cardCategory != "DEFENSIVE_SHIELD")]' \
card_blueprint_database.json > /tmp/card_blueprint_database_dark_no_shields.json
# Verify filtering
echo "Light side shields (should be 0):"
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' \
/tmp/card_blueprint_database_light_no_shields.json
echo "Dark side shields (should be 0):"
jq '[.[] | select(.cardCategory == "DEFENSIVE_SHIELD")] | length' \
/tmp/card_blueprint_database_dark_no_shields.json
BASH
# 5. Map all cards with side-specific databases
CUBE_DIR="src/gemp-swccg-server/src/main/resources/draft/cube_building/my_cube"
# Default cards - use main database
./bin/map_card_names_to_ids \
$CUBE_DIR/default_cards.csv \
$CUBE_DIR/default_cards_mapped.json \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json
# Light side - use filtered light-only database
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_lightside.csv \
$CUBE_DIR/cube_worlds_lightside_mapped.json \
/tmp/card_blueprint_database_light_no_shields.json
# Dark side - use filtered dark-only database
./bin/map_card_names_to_ids \
$CUBE_DIR/cube_worlds_darkside.csv \
$CUBE_DIR/cube_worlds_darkside_mapped.json \
/tmp/card_blueprint_database_dark_no_shields.json
# 6. Generate config
python3 bin/create_cube $CUBE_DIR src/gemp-swccg-server/src/main/resources/draft/cubeMyCube.json
# 7. Verify no playtesting or defensive shields
echo "Playtesting cards (should be 0):"
grep -c '"501_' src/gemp-swccg-server/src/main/resources/draft/cubeMyCube.json || echo "0"
echo "Checking defensive shields are not in cube..."
# This checks a sample defensive shield - expand as needed
bash <<'BASH'
CUBE_FILE="src/gemp-swccg-server/src/main/resources/draft/cubeMyCube.json"
SHIELD_COUNT=0
for id in $(jq -r '.[] | select(.cardCategory == "DEFENSIVE_SHIELD") | .cardId' \
src/gemp-swccg-cards/src/main/resources/card_blueprint_database.json | head -10); do
if grep -q "\"$id\"" "$CUBE_FILE"; then
SHIELD_COUNT=$((SHIELD_COUNT + 1))
fi
done
echo "Defensive shields found: $SHIELD_COUNT (should be 0)"
BASH
# 8. Register in swccgDrafts.json
# Add: {"type": "my_cube_draft", "location": "/draft/cubeMyCube.json"}
# 9. Add to leagueAdmin.html
# Add: <option value="my_cube_draft">My Cube Draft</option>
# 10. Deploy
./bin/gemp reload-fast
See references/ directory for additional documentation on cube draft mechanics and troubleshooting scenarios.
npx skills add PlayersCommittee/cube-builder下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer