Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SCPSL Map Template Exporter

SCPSL Map Template Exporter is a LabAPI server plugin for SCP: Secret Laboratory. After the server finishes generating a facility, the plugin exports the runtime map-generation inputs and the resulting room layout as JSON files.

The exported data is intended for offline tools that need to inspect, visualize, compare, or reproduce the room-generation process without running the game server.

What It Exports

The plugin creates up to two files after map generation:

mapgen-raw-<gameVersion>.json
mapgen-snapshot-<gameVersion>-seed-<seed>.json

For example:

mapgen-raw-14.2.7.json
mapgen-snapshot-14.2.7-seed-123456.json

Raw Template File

The raw file contains the version-specific inputs used by the facility generator:

  • game version, build type, and active holiday;
  • facility grid scale;
  • glyph colors, room shapes, specific-room constraints, and allowed rotations;
  • zone generators in their original runtime order;
  • atlas textures encoded as bottom-left-origin RGBA8 data;
  • atlas SHA-256 hashes;
  • compatible room candidates in their original order;
  • room selection limits and probability weights;
  • single-room generator positions and rotations;
  • holiday room variants, including serialized null variants;
  • every room prefab's connector spawnpoints, including inactive spawnpoints.

Simplified structure:

{
  "schemaVersion": 1,
  "game": {
    "version": "14.2.7",
    "buildType": "Release",
    "activeHoliday": "None"
  },
  "exportedAtUtc": "2026-08-06T00:00:00.0000000Z",
  "seedUsedForExport": 123456,
  "pixelLayout": "RGBA8,row-major,bottom-left-origin",
  "gridScale": { "x": 15, "y": 100, "z": 15 },
  "glyphShapePairs": [],
  "generators": [],
  "roomTemplates": []
}

An atlas texture is stored as follows:

{
  "index": 0,
  "name": "HCZ_Atlas_00",
  "width": 32,
  "height": 32,
  "isReadable": true,
  "encoding": "rgba8-base64",
  "origin": "bottom-left",
  "rgbaBase64": "...",
  "sha256": "..."
}

Decode rgbaBase64 into exactly width * height * 4 bytes. Pixel (x, y) starts at this byte offset:

(y * width + x) * 4

The four bytes are red, green, blue, and alpha. The first row is the bottom row of the Unity texture.

An atlas generator contains its source atlases and ordered room candidates:

{
  "index": 0,
  "kind": "atlas",
  "runtimeType": "MapGeneration.AtlasZoneGenerator",
  "targetZone": { "name": "HeavyContainment", "value": 2 },
  "zoneHeight": 0,
  "atlases": [],
  "compatibleRoomTemplateIds": [
    "room-template-000",
    "room-template-001"
  ],
  "compatibleRooms": [
    { "index": 0, "templateId": "room-template-000" },
    { "index": 1, "templateId": "room-template-001" }
  ]
}

Single-room generators preserve the serialized rotation and also provide a usable normalized rotation:

{
  "index": 3,
  "kind": "single-room",
  "prefabTemplateId": "room-template-042",
  "spawnPosition": { "x": 0, "y": 1000, "z": 0 },
  "spawnRotation": { "x": 0, "y": 0, "z": 0, "w": 0 },
  "effectiveRotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
  "effectiveRotationY": 0
}

spawnRotation is the original serialized value. effectiveRotation is normalized. A zero quaternion is interpreted as the identity quaternion. effectiveRotationY is the corresponding yaw angle in degrees.

Holiday variants explicitly distinguish a serialized null reference from a resolved room template:

{
  "index": 0,
  "holiday": { "name": "Christmas", "value": 1 },
  "resultState": "serialized-null",
  "templateId": null
}

Non-null variants use "resultState": "resolved". If a non-null result cannot be mapped to an exported room template, the export fails instead of writing an ambiguous null reference.

Each unique room template contains its connector geometry. localPosition and localRotation are relative to the SpawnableRoom prefab root, even when the connector is nested below intermediate child objects:

{
  "id": "room-template-000",
  "prefabName": "LCZ_Straight",
  "connectorPoints": [
    {
      "index": 0,
      "runtimeType": "MapGeneration.RoomConnectors.Spawners.SimpleRoomConnectorSpawnpoint",
      "activeSelf": true,
      "localPosition": { "x": 0, "y": 0, "z": 7.5 },
      "localRotation": { "x": 0, "y": 0, "z": 0, "w": 1 },
      "fallbackType": { "name": "LczStandardDoor", "value": 3 },
      "connectorPriority": 0,
      "spawnPriority": 0,
      "allowedTypes": [
        { "name": "LczStandardDoor", "value": 3 },
        { "name": "OpenHallway", "value": 6 }
      ]
    }
  ]
}

Advanced spawnpoints use a connectors array. Each entry preserves its serialized order, chance weight, and allowed connector types:

{
  "connectors": [
    {
      "index": 0,
      "chance": 1.5,
      "allowedTypes": [
        { "name": "HczStandardDoor", "value": 2 }
      ]
    }
  ]
}

Room templates are collected from every atlas generator, every single-room generator, and all recursively referenced holiday variants. Connector collection does not invoke map generation, atlas interpretation, or random-number APIs. A missing required connector field fails the export instead of publishing an empty placeholder array.

Seed Snapshot File

The snapshot file records the facility that the server actually created for one seed. It is the authoritative expected result for testing an offline generator.

It contains:

  • the exact seed;
  • every runtime RoomIdentifier;
  • world position and quaternion rotation;
  • normalized Y rotation;
  • facility grid coordinates;
  • room name, shape, and zone;
  • source SpawnableRoom name and duplicate ID;
  • every runtime connector's world position and quaternion rotation;
  • room-to-room connections;
  • the ordered spawn trace for every atlas generator.

Simplified structure:

{
  "schemaVersion": 1,
  "game": {
    "version": "14.2.7",
    "buildType": "Release",
    "activeHoliday": "None"
  },
  "exportedAtUtc": "2026-08-06T00:00:00.0000000Z",
  "seed": 123456,
  "rooms": [],
  "spawnTrace": []
}

A room entry looks like this:

{
  "id": "room-000",
  "position": { "x": 0, "y": 0, "z": 15 },
  "rotationY": 90,
  "rotation": { "x": 0, "y": 0.7071068, "z": 0, "w": 0.7071068 },
  "mainCoords": { "x": 0, "y": 0, "z": 1 },
  "name": { "name": "HczCrossing", "value": 20 },
  "shape": { "name": "XShape", "value": 4 },
  "zone": { "name": "HeavyContainment", "value": 2 },
  "spawnableRoomName": "HCZ_Crossing",
  "duplicateId": 0,
  "connectorInstances": [
    {
      "index": 0,
      "runtimeType": "MapGeneration.RoomConnectors.Spawners.SimpleRoomConnectorSpawnpoint",
      "worldPosition": { "x": 0, "y": 0, "z": 22.5 },
      "worldRotation": { "x": 0, "y": 0.7071068, "z": 0, "w": 0.7071068 },
      "connectedRoomIds": ["room-001"]
    },
    {
      "index": 1,
      "runtimeType": "MapGeneration.RoomConnectors.Spawners.SimpleRoomConnectorSpawnpoint",
      "worldPosition": { "x": 7.5, "y": 0, "z": 15 },
      "worldRotation": { "x": 0, "y": 1, "z": 0, "w": 0 },
      "connectedRoomIds": ["room-004"]
    }
  ],
  "connectedRoomIds": ["room-001", "room-004"]
}

Snapshot room IDs are stable within the file and do not use Unity instance IDs. The exporter verifies that RoomIdentifier.ConnectedRooms is bidirectional and that the union of each room's connector-level connectedRoomIds matches the room-level connectedRoomIds.

The spawn trace records the generator's actual ordered decisions:

{
  "generatorIndex": 0,
  "spawnedIndex": 0,
  "coords": { "x": 0, "y": 1 },
  "roomShape": { "name": "Straight", "value": 2 },
  "specificRooms": [],
  "rotationY": 90,
  "chosenCandidateTemplateId": "room-template-005",
  "instanceRoomId": "room-000"
}

Offline Connector Validation

Use the raw template file to transform each connector into world space:

worldConnector = room.position + room.rotation * connector.localPosition

Two active connector points represent the same opening when their squared world-space distance is at most 1. Build bidirectional room edges from those matches and compare them with both connectorInstances and connectedRoomIds in the seed snapshot. The connector's X/Z direction from the room center can then be mapped to north, east, south, or west for visualization.

RoomShape, grid adjacency, and rotationY alone are not authoritative opening data. Do not hard-code T-shape, curve, or crossing directions from those fields.

How to Install and Use

  1. Stop the dedicated server.
  2. Copy SCPSL_Template_export.dll into the LabAPI global plugin directory.
  3. Start the server and check for this log message:
[MapTemplateExporter] Waiting for map generation to finish.
  1. Allow the server to generate a map. No command is required.
  2. Read the final file paths from the server console.

On Windows, the global plugin directory is normally under:

%APPDATA%/SCP Secret Laboratory/LabAPI/plugins/global/

The plugin writes files only after SeedSynchronizer.OnGenerationFinished, when room coordinates and connections have been registered.

Configuration

LabAPI creates the plugin configuration on first load:

enabled: true
export_raw_templates: true
export_seed_snapshot: true
overwrite_existing_raw: false
pretty_print: false
output_directory: ''
Setting Meaning
enabled Enables event subscription and exporting.
export_raw_templates Writes the version-level raw template file.
export_seed_snapshot Writes the current seed snapshot.
overwrite_existing_raw Allows replacing an existing raw file for the same game version.
pretty_print Produces indented JSON for manual inspection.
output_directory Uses a custom output directory. Relative paths are resolved from the plugin configuration directory.

When output_directory is empty, files are written to the plugin configuration directory's exports subdirectory.

Raw files are not overwritten by default. Snapshot files for the same game version and seed are replaced atomically. Temporary files are removed if serialization or validation fails.

About

LabAPI plugin for exporting SCP: Secret Laboratory map-generation templates, room connectors, and seed snapshots for offline analysis and visualization

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages