- docs/mapmaking/00-index.md — SDK overview with workflow diagram - docs/mapmaking/01-getting-started.md — template setup & first map - docs/mapmaking/02-building-geometry.md — CSG guide & prefab reference - docs/mapmaking/03-lighting-and-env.md — LightmapGI baking guide - docs/mapmaking/04-validation.md — validator CLI & CI/CD usage - docs/mapmaking/05-packaging-and-shipping.md — .pck pipeline - docs/mapmaking/06-faq-and-troubleshooting.md — 30+ common issues - README.md — add mapmaking SDK link to features list
7.9 KiB
Packaging & Shipping
Once your map is built, validated, and baked, it's time to publish it to the
world. Tactical Shooter uses the PCK (Godot Resource Pack) format — the map
is packaged as a .pck file, uploaded to a registry server, and clients
download it automatically when they join a server running that map.
Pipeline Overview
┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐
│ pack_map │ → │ .pck + .json │ → │ Map Registry │ → │ Client │
│ .gd │ │ files │ → │ Server │ → │ Downloader │
└──────────┘ └──────────────┘ └──────────────┘ └────────────┘
(editor (artifacts) (HTTP serving) (autoload)
tool)
Step 1: Package the Map
The pack_map.gd script (at scripts/map_packaging/pack_map.gd) is a Godot
editor tool that exports a .tscn scene as a standalone .pck resource pack.
Usage
From the map template project (not the main game project):
cd maps/my_first_map/
godot --headless --script /path/to/scripts/map_packaging/pack_map.gd \
--map=res://de_my_map.tscn
Replace
/path/to/with the actual path to the tactical-shooter repository root, or copypack_map.gdinto your map project'sscripts/directory.
What It Does
- Loads the specified
.tscnscene - Recursively collects all dependencies (materials, textures, meshes, etc.)
- Exports them to
user://packed_maps/de_my_map.pck - Writes a metadata file
user://packed_maps/de_my_map.json
Output Files
user://packed_maps/
├── de_my_map.pck # Binary resource pack (~1–10 MB for a typical map)
└── de_my_map.json # JSON metadata (machine-readable)
Metadata example (de_my_map.json):
{
"map_name": "de_my_map",
"source_scene": "res://de_my_map.tscn",
"godot_version": {
"major": 4,
"minor": 2,
"string": "4.2"
},
"packed_at": "2026-06-25T14:30:00"
}
Requirements
- The map scene must use local resources only (relative
res://paths) - External dependencies (game shared assets like weapon models, player
models) should stay in the base game — the
.pckis additive content - The
.pckcontains only the map's unique assets, not the entire game
From the Editor
You can also run pack_map.gd from the Godot editor:
- Open your map project in Godot
- Open your map scene (
de_my_map.tscn) - Project → Tools → Pack Current Map (if configured)
- The script detects the active scene and exports it
Step 2: Set Up the Map Registry Server
The registry server is a lightweight Python HTTP server
(scripts/map_packaging/map_registry_server.py) that:
- Serves
.pckfiles for download - Provides a JSON
/mapsendpoint listing available maps - Auto-scans the maps directory every 5 seconds
- Computes and serves SHA-256 checksums for integrity verification
Starting the Server
cd /path/to/tactical-shooter
# Default: port 8090, maps from ./packed_maps/
python3 scripts/map_packaging/map_registry_server.py
# Custom configuration
python3 scripts/map_packaging/map_registry_server.py \
--port 8080 \
--maps-dir /data/tactical-shooter-maps \
--verbose
Environment variables:
| Variable | Overrides | Default |
|---|---|---|
MAP_REGISTRY_PORT |
--port / -p |
8090 |
MAP_REGISTRY_MAPS |
--maps-dir / -d |
./packed_maps/ |
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
GET / |
— | Server info and endpoint documentation |
GET /maps |
— | JSON list of all available maps with metadata |
GET /maps/<name>.pck |
— | Download map .pck binary |
GET /maps/<name>.json |
— | Download per-map metadata |
OPTIONS /maps |
— | CORS preflight |
Example responses:
GET /maps
{
"maps": [
{
"name": "de_dust2",
"size": 5432100,
"version": 1,
"description": "Classic competitive map",
"checksum_sha256": "a1b2c3d4...",
"packed_at": "2026-06-25T14:30:00"
}
],
"server_name": "Tactical Shooter Map Registry",
"server_version": "1.0.0",
"map_count": 1
}
GET /maps/de_dust2.pck
→ Returns the .pck binary file as an octet-stream download.
Deployment Options
- Local dev: Run on the same machine as the game (default
127.0.0.1:8090) - Server host: Run on a public server alongside the master server
- Docker: Wrap in a Docker container for easy deployment
- Systemd service: Create a service file for production hosting
Systemd service example:
[Unit]
Description=Tactical Shooter Map Registry
After=network.target
[Service]
Type=simple
User=tactical
WorkingDirectory=/opt/tactical-shooter
ExecStart=/usr/bin/python3 scripts/map_packaging/map_registry_server.py \
--port 8090 --maps-dir /data/tactical-maps
Restart=on-failure
[Install]
WantedBy=multi-user.target
Step 3: Upload Your Map
- Copy the
.pckand.jsonfiles to the registry server's maps directory:cp user://packed_maps/de_my_map.pck /data/tactical-maps/ cp user://packed_maps/de_my_map.json /data/tactical-maps/ - The server auto-detects new files within 5 seconds
- Verify via
curl http://your-server:8090/maps
Step 4: Client Downloads
The client uses MapDownloader (autoload at client/scripts/map_downloader.gd)
to fetch and load maps at runtime.
How It Works
- Player joins a server that specifies a map name
- The client checks its local cache (
user://maps/manifest.json) - If the map is not cached,
MapDownloader.fetch_map_list()polls the registry server to verify the map exists MapDownloader.download_map(name)downloads the.pcktouser://maps/ProjectSettings.load_resource_pack()loads it into the game- The server changes to the new level
Map Caching
user://maps/
├── de_dust2.pck # Cached map binary
├── de_inferno.pck # Another cached map
└── manifest.json # Cache index
Cache behaviour:
- Maps are cached to disk after download (survives restarts)
- The manifest tracks version numbers for staleness checks
MapDownloader.remove_map(name)clears a specific mapMapDownloader.clear_cache()removes all cached maps
Configuration
The MapDownloader autoload can be configured in-game:
# Set the registry server URL (override default http://127.0.0.1:8090)
MapDownloader.registry_url = "http://maps.myserver.com:8090"
The URL can also be set via the MAP_REGISTRY_URL environment variable,
which is read in _ready().
Signals
# Register for map lifecycle events
MapDownloader.map_list_loaded.connect(_on_map_list)
MapDownloader.map_download_progress.connect(_on_download_progress)
MapDownloader.map_download_complete.connect(_on_download_done)
MapDownloader.map_loaded.connect(_on_map_loaded)
Full Pipeline Example
# 1. Package from the template project
cd maps/my_first_map
godot --headless --script ../../scripts/map_packaging/pack_map.gd \
--map=res://de_my_map.tscn
# 2. Start registry server
cd ../..
python3 scripts/map_packaging/map_registry_server.py --port 8090 &
# 3. Deploy map
cp ~/.local/share/godot/app_userdata/MapTemplate/packed_maps/de_my_map.pck \
scripts/map_packaging/packed_maps/
cp ~/.local/share/godot/app_userdata/MapTemplate/packed_maps/de_my_map.json \
scripts/map_packaging/packed_maps/
# 4. Verify
curl -s http://localhost:8090/maps | python3 -m json.tool
# 5. In-game: connect to server and play the map
Next Steps
- FAQ & Troubleshooting — common issues