Files
tactical-shooter/docs/mapmaking/05-packaging-and-shipping.md
T
shawn 34507f9043 docs: community mapmaking documentation (7-file SDK guide)
- 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
2026-07-01 18:47:06 -04:00

7.9 KiB
Raw Blame History

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 copy pack_map.gd into your map project's scripts/ directory.

What It Does

  1. Loads the specified .tscn scene
  2. Recursively collects all dependencies (materials, textures, meshes, etc.)
  3. Exports them to user://packed_maps/de_my_map.pck
  4. Writes a metadata file user://packed_maps/de_my_map.json

Output Files

user://packed_maps/
├── de_my_map.pck          # Binary resource pack (~110 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 .pck is additive content
  • The .pck contains only the map's unique assets, not the entire game

From the Editor

You can also run pack_map.gd from the Godot editor:

  1. Open your map project in Godot
  2. Open your map scene (de_my_map.tscn)
  3. Project → Tools → Pack Current Map (if configured)
  4. 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 .pck files for download
  • Provides a JSON /maps endpoint 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

  1. Copy the .pck and .json files 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/
    
  2. The server auto-detects new files within 5 seconds
  3. 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

  1. Player joins a server that specifies a map name
  2. The client checks its local cache (user://maps/manifest.json)
  3. If the map is not cached, MapDownloader.fetch_map_list() polls the registry server to verify the map exists
  4. MapDownloader.download_map(name) downloads the .pck to user://maps/
  5. ProjectSettings.load_resource_pack() loads it into the game
  6. 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 map
  • MapDownloader.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