~starkingdoms/starkingdoms

dd101bc47e02b050cc85e160450af537e601d8fe — core 2 years ago e459b36
whoa, i just rewrote spacetime again
54 files changed, 1129 insertions(+), 392 deletions(-)

M Cargo.lock
M Cargo.toml
A ansible/infra
M assets/dist/spritesheet-125.json
M assets/dist/spritesheet-125.png
M assets/dist/spritesheet-375.json
M assets/dist/spritesheet-375.png
M assets/dist/spritesheet-full.json
M assets/dist/spritesheet-full.png
M assets/final/125/earth.png
M assets/final/125/moon.png
M assets/final/375/earth.png
M assets/final/375/moon.png
M assets/final/full/earth.png
M assets/final/full/moon.png
R assets/src/{autoplr_cfg.ink => autoplr_cfg}.svg
R assets/src/{autoplr_error.ink => autoplr_error}.svg
R assets/src/{autoplr_on.ink => autoplr_on}.svg
R assets/src/{cargo_off.ink => cargo_off}.svg
R assets/src/{cargo_on.ink => cargo_on}.svg
R assets/src/{earth.ink => earth}.svg
R assets/src/{ecothruster_on.ink => ecothruster_on}.svg
R assets/src/{hearty.ink => hearty}.svg
R assets/src/{hub_off.ink => hub_off}.svg
R assets/src/{hub_on.ink => hub_on}.svg
R assets/src/{landingleg.ink => landingleg}.svg
R assets/src/{landingthruster_off.ink => landingthruster_off}.svg
R assets/src/{landingthruster_on.ink => landingthruster_on}.svg
R assets/src/{moon.ink => moon}.svg
R assets/src/{powerhub_off.ink => powerhub_off}.svg
R assets/src/{powerhub_on.ink => powerhub_on}.svg
R assets/src/{starfield.ink => starfield}.svg
R assets/src/{superthruster_off.ink => superthruster_off}.svg
R assets/src/{superthruster_on.ink => superthruster_on}.svg
R assets/src/{thruster_off.ink => thruster_off}.svg
R assets/src/{thruster_on.ink => thruster_on}.svg
M spacetime
A spacetime_old
A spacetime_rs/Cargo.toml
A spacetime_rs/src/cmd.rs
A spacetime_rs/src/commands/api.rs
A spacetime_rs/src/commands/assets.rs
A spacetime_rs/src/commands/clean.rs
A spacetime_rs/src/commands/client.rs
A spacetime_rs/src/commands/docker.rs
A spacetime_rs/src/commands/mod.rs
A spacetime_rs/src/commands/server.rs
A spacetime_rs/src/config.rs
A spacetime_rs/src/configure/asset.rs
A spacetime_rs/src/configure/client.rs
A spacetime_rs/src/configure/mod.rs
A spacetime_rs/src/configure/rust.rs
A spacetime_rs/src/main.rs
A spacetime_rs/src/ninja.rs
M Cargo.lock => Cargo.lock +17 -0
@@ 3164,6 3164,14 @@ dependencies = [
]

[[package]]
name = "spacetime"
version = "0.1.0"
dependencies = [
 "tabwriter",
 "which",
]

[[package]]
name = "spade"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3400,6 3408,15 @@ dependencies = [
]

[[package]]
name = "tabwriter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36205cfc997faadcc4b0b87aaef3fbedafe20d38d4959a7ca6ff803564051111"
dependencies = [
 "unicode-width",
]

[[package]]
name = "tempfile"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"

M Cargo.toml => Cargo.toml +2 -1
@@ 4,7 4,8 @@ members = [
    "protocol",
    "api",
    "api/starkingdoms_api_entities",
    "api/starkingdoms_api_migration"
    "api/starkingdoms_api_migration",
    "spacetime_rs"
]

[profile.dev.package.rapier2d-f64]

A ansible/infra => ansible/infra +3 -0
@@ 0,0 1,3 @@
#!/bin/bash
echo "[*] Connecting to infrastructure manager server. If you are prompted for a password, enter your infrastructure key. You may be prompted several times."
ssh team@10.16.1.3 /home/team/run_ansible.sh "$1"
\ No newline at end of file

M assets/dist/spritesheet-125.json => assets/dist/spritesheet-125.json +37 -37
@@ 1,25 1,25 @@
{
  "frames": {
    "moon.png": {
      "frame": { "x": 0, "y": 0, "w": 256, "h": 256 },
    "starfield.png": {
      "frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 256, "h": 256 },
      "sourceSize": { "w": 256, "h": 256 },
      "pivot": { "x": 128, "y": 128 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 256, "h": 256 }
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
      "sourceSize": { "w": 64, "h": 64 },
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "earth.png": {
      "frame": { "x": 0, "y": 256, "w": 256, "h": 256 },
    "moon.png": {
      "frame": { "x": 0, "y": 64, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 256, "h": 256 },
      "sourceSize": { "w": 256, "h": 256 },
      "pivot": { "x": 128, "y": 128 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 256, "h": 256 }
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
      "sourceSize": { "w": 64, "h": 64 },
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "starfield.png": {
      "frame": { "x": 0, "y": 512, "w": 64, "h": 64 },
    "earth.png": {
      "frame": { "x": 0, "y": 128, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 27,8 27,8 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "autoplr_cfg.png": {
      "frame": { "x": 0, "y": 576, "w": 64, "h": 64 },
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 192, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 36,8 36,8 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 640, "w": 64, "h": 64 },
    "autoplr_cfg.png": {
      "frame": { "x": 0, "y": 256, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 46,7 46,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "hearty.png": {
      "frame": { "x": 0, "y": 704, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 320, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 55,7 55,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "superthruster_on.png": {
      "frame": { "x": 0, "y": 768, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 384, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 64,7 64,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "ecothruster_on.png": {
      "frame": { "x": 0, "y": 832, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 448, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 73,7 73,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "landingthruster_on.png": {
      "frame": { "x": 0, "y": 896, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 512, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 82,7 82,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "thruster_on.png": {
      "frame": { "x": 0, "y": 960, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 576, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 91,7 91,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "landingleg.png": {
      "frame": { "x": 0, "y": 1024, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 640, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 99,8 99,8 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "autoplr_on.png": {
      "frame": { "x": 0, "y": 1088, "w": 64, "h": 64 },
    "hub_on.png": {
      "frame": { "x": 0, "y": 704, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 108,8 108,8 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "hub_on.png": {
      "frame": { "x": 0, "y": 1152, "w": 64, "h": 64 },
    "powerhub_on.png": {
      "frame": { "x": 0, "y": 768, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 117,8 117,8 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "powerhub_on.png": {
      "frame": { "x": 0, "y": 1216, "w": 64, "h": 64 },
    "autoplr_on.png": {
      "frame": { "x": 0, "y": 832, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 127,7 127,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "superthruster_off.png": {
      "frame": { "x": 0, "y": 1280, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 896, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 136,7 136,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "landingthruster_off.png": {
      "frame": { "x": 0, "y": 1344, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 960, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 145,7 145,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "thruster_off.png": {
      "frame": { "x": 0, "y": 1408, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 1024, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 154,7 154,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "cargo_on.png": {
      "frame": { "x": 0, "y": 1472, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 1088, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 163,7 163,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "cargo_off.png": {
      "frame": { "x": 0, "y": 1536, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 1152, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 172,7 172,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "powerhub_off.png": {
      "frame": { "x": 0, "y": 1600, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 1216, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },


@@ 181,7 181,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "hub_off.png": {
      "frame": { "x": 0, "y": 1664, "w": 64, "h": 64 },
      "frame": { "x": 0, "y": 1280, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },

M assets/dist/spritesheet-125.png => assets/dist/spritesheet-125.png +0 -0
M assets/dist/spritesheet-375.json => assets/dist/spritesheet-375.json +39 -39
@@ 1,25 1,25 @@
{
  "frames": {
    "moon.png": {
      "frame": { "x": 0, "y": 0, "w": 768, "h": 768 },
    "starfield.png": {
      "frame": { "x": 0, "y": 0, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 768, "h": 768 },
      "sourceSize": { "w": 768, "h": 768 },
      "pivot": { "x": 384, "y": 384 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 768, "h": 768 }
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
      "sourceSize": { "w": 192, "h": 192 },
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "earth.png": {
      "frame": { "x": 0, "y": 768, "w": 768, "h": 768 },
    "moon.png": {
      "frame": { "x": 0, "y": 192, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 768, "h": 768 },
      "sourceSize": { "w": 768, "h": 768 },
      "pivot": { "x": 384, "y": 384 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 768, "h": 768 }
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
      "sourceSize": { "w": 192, "h": 192 },
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "starfield.png": {
      "frame": { "x": 0, "y": 1536, "w": 192, "h": 192 },
    "earth.png": {
      "frame": { "x": 0, "y": 384, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 27,8 27,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "autoplr_cfg.png": {
      "frame": { "x": 0, "y": 1728, "w": 192, "h": 192 },
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 576, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 36,8 36,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 1920, "w": 192, "h": 192 },
    "autoplr_cfg.png": {
      "frame": { "x": 0, "y": 768, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 46,7 46,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "hearty.png": {
      "frame": { "x": 0, "y": 2112, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 960, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 55,7 55,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "superthruster_on.png": {
      "frame": { "x": 0, "y": 2304, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 1152, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 64,7 64,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "ecothruster_on.png": {
      "frame": { "x": 0, "y": 2496, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 1344, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 73,7 73,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "landingthruster_on.png": {
      "frame": { "x": 0, "y": 2688, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 1536, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 82,7 82,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "thruster_on.png": {
      "frame": { "x": 0, "y": 2880, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 1728, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 91,7 91,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "landingleg.png": {
      "frame": { "x": 0, "y": 3072, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 1920, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 99,8 99,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "autoplr_on.png": {
      "frame": { "x": 0, "y": 3264, "w": 192, "h": 192 },
    "hub_on.png": {
      "frame": { "x": 0, "y": 2112, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 108,8 108,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "hub_on.png": {
      "frame": { "x": 0, "y": 3456, "w": 192, "h": 192 },
    "powerhub_on.png": {
      "frame": { "x": 0, "y": 2304, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 117,8 117,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "powerhub_on.png": {
      "frame": { "x": 0, "y": 3648, "w": 192, "h": 192 },
    "autoplr_on.png": {
      "frame": { "x": 0, "y": 2496, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 127,7 127,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "landingthruster_off.png": {
      "frame": { "x": 0, "y": 3840, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 2688, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 136,7 136,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "superthruster_off.png": {
      "frame": { "x": 192, "y": 1536, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 2880, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 145,7 145,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "thruster_off.png": {
      "frame": { "x": 192, "y": 1728, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 3072, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 154,7 154,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "cargo_on.png": {
      "frame": { "x": 192, "y": 1920, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 3264, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 163,7 163,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "cargo_off.png": {
      "frame": { "x": 192, "y": 2112, "w": 192, "h": 192 },
      "frame": { "x": 0, "y": 3456, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 171,8 171,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "powerhub_off.png": {
      "frame": { "x": 192, "y": 2304, "w": 192, "h": 192 },
    "hub_off.png": {
      "frame": { "x": 0, "y": 3648, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },


@@ 180,8 180,8 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "hub_off.png": {
      "frame": { "x": 192, "y": 2496, "w": 192, "h": 192 },
    "powerhub_off.png": {
      "frame": { "x": 0, "y": 3840, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },

M assets/dist/spritesheet-375.png => assets/dist/spritesheet-375.png +0 -0
M assets/dist/spritesheet-full.json => assets/dist/spritesheet-full.json +39 -39
@@ 1,25 1,25 @@
{
  "frames": {
    "moon.png": {
      "frame": { "x": 0, "y": 0, "w": 2048, "h": 2048 },
    "starfield.png": {
      "frame": { "x": 0, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 2048, "h": 2048 },
      "sourceSize": { "w": 2048, "h": 2048 },
      "pivot": { "x": 1024, "y": 1024 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 2048, "h": 2048 }
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
      "sourceSize": { "w": 512, "h": 512 },
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "earth.png": {
      "frame": { "x": 0, "y": 2048, "w": 2048, "h": 2048 },
    "moon.png": {
      "frame": { "x": 0, "y": 512, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 2048, "h": 2048 },
      "sourceSize": { "w": 2048, "h": 2048 },
      "pivot": { "x": 1024, "y": 1024 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 2048, "h": 2048 }
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
      "sourceSize": { "w": 512, "h": 512 },
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "starfield.png": {
      "frame": { "x": 2048, "y": 0, "w": 512, "h": 512 },
    "earth.png": {
      "frame": { "x": 0, "y": 1024, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 27,8 27,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "autoplr_cfg.png": {
      "frame": { "x": 2560, "y": 0, "w": 512, "h": 512 },
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 1536, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 36,8 36,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "autoplr_error.png": {
      "frame": { "x": 3072, "y": 0, "w": 512, "h": 512 },
    "autoplr_cfg.png": {
      "frame": { "x": 0, "y": 2048, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 46,7 46,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "hearty.png": {
      "frame": { "x": 3584, "y": 0, "w": 512, "h": 512 },
      "frame": { "x": 0, "y": 2560, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 55,7 55,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "superthruster_on.png": {
      "frame": { "x": 2048, "y": 512, "w": 512, "h": 512 },
      "frame": { "x": 0, "y": 3072, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 64,7 64,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "ecothruster_on.png": {
      "frame": { "x": 2560, "y": 512, "w": 512, "h": 512 },
      "frame": { "x": 0, "y": 3584, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 73,7 73,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "landingthruster_on.png": {
      "frame": { "x": 3072, "y": 512, "w": 512, "h": 512 },
      "frame": { "x": 512, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 82,7 82,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "thruster_on.png": {
      "frame": { "x": 3584, "y": 512, "w": 512, "h": 512 },
      "frame": { "x": 1024, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 91,7 91,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "landingleg.png": {
      "frame": { "x": 2048, "y": 1024, "w": 512, "h": 512 },
      "frame": { "x": 1536, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 99,8 99,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "autoplr_on.png": {
      "frame": { "x": 2560, "y": 1024, "w": 512, "h": 512 },
    "hub_on.png": {
      "frame": { "x": 2048, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 108,8 108,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "hub_on.png": {
      "frame": { "x": 3072, "y": 1024, "w": 512, "h": 512 },
    "powerhub_on.png": {
      "frame": { "x": 2560, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 117,8 117,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "powerhub_on.png": {
      "frame": { "x": 3584, "y": 1024, "w": 512, "h": 512 },
    "autoplr_on.png": {
      "frame": { "x": 3072, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 127,7 127,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "superthruster_off.png": {
      "frame": { "x": 2048, "y": 1536, "w": 512, "h": 512 },
      "frame": { "x": 3584, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 136,7 136,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "landingthruster_off.png": {
      "frame": { "x": 2560, "y": 1536, "w": 512, "h": 512 },
      "frame": { "x": 512, "y": 512, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 145,7 145,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "thruster_off.png": {
      "frame": { "x": 3072, "y": 1536, "w": 512, "h": 512 },
      "frame": { "x": 512, "y": 1024, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 154,7 154,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "cargo_on.png": {
      "frame": { "x": 3584, "y": 1536, "w": 512, "h": 512 },
      "frame": { "x": 512, "y": 1536, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 163,7 163,7 @@
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "cargo_off.png": {
      "frame": { "x": 2048, "y": 2048, "w": 512, "h": 512 },
      "frame": { "x": 512, "y": 2048, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 171,8 171,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "powerhub_off.png": {
      "frame": { "x": 2048, "y": 2560, "w": 512, "h": 512 },
    "hub_off.png": {
      "frame": { "x": 512, "y": 2560, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },


@@ 180,8 180,8 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "hub_off.png": {
      "frame": { "x": 2048, "y": 3072, "w": 512, "h": 512 },
    "powerhub_off.png": {
      "frame": { "x": 512, "y": 3072, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },

M assets/dist/spritesheet-full.png => assets/dist/spritesheet-full.png +0 -0
M assets/final/125/earth.png => assets/final/125/earth.png +0 -0
M assets/final/125/moon.png => assets/final/125/moon.png +0 -0
M assets/final/375/earth.png => assets/final/375/earth.png +0 -0
M assets/final/375/moon.png => assets/final/375/moon.png +0 -0
M assets/final/full/earth.png => assets/final/full/earth.png +0 -0
M assets/final/full/moon.png => assets/final/full/moon.png +0 -0
R assets/src/autoplr_cfg.ink.svg => assets/src/autoplr_cfg.svg +0 -0
R assets/src/autoplr_error.ink.svg => assets/src/autoplr_error.svg +0 -0
R assets/src/autoplr_on.ink.svg => assets/src/autoplr_on.svg +0 -0
R assets/src/cargo_off.ink.svg => assets/src/cargo_off.svg +0 -0
R assets/src/cargo_on.ink.svg => assets/src/cargo_on.svg +0 -0
R assets/src/earth.ink.svg => assets/src/earth.svg +0 -0
R assets/src/ecothruster_on.ink.svg => assets/src/ecothruster_on.svg +0 -0
R assets/src/hearty.ink.svg => assets/src/hearty.svg +0 -0
R assets/src/hub_off.ink.svg => assets/src/hub_off.svg +0 -0
R assets/src/hub_on.ink.svg => assets/src/hub_on.svg +0 -0
R assets/src/landingleg.ink.svg => assets/src/landingleg.svg +0 -0
R assets/src/landingthruster_off.ink.svg => assets/src/landingthruster_off.svg +0 -0
R assets/src/landingthruster_on.ink.svg => assets/src/landingthruster_on.svg +0 -0
R assets/src/moon.ink.svg => assets/src/moon.svg +0 -0
R assets/src/powerhub_off.ink.svg => assets/src/powerhub_off.svg +0 -0
R assets/src/powerhub_on.ink.svg => assets/src/powerhub_on.svg +0 -0
R assets/src/starfield.ink.svg => assets/src/starfield.svg +0 -0
R assets/src/superthruster_off.ink.svg => assets/src/superthruster_off.svg +0 -0
R assets/src/superthruster_on.ink.svg => assets/src/superthruster_on.svg +0 -0
R assets/src/thruster_off.ink.svg => assets/src/thruster_off.svg +0 -0
R assets/src/thruster_on.ink.svg => assets/src/thruster_on.svg +0 -0
M spacetime => spacetime +2 -276
@@ 4,281 4,7 @@ set -e

SCRIPT_PATH=$(readlink -f "${BASH_SOURCE:-$0}")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
SERVER_MODS=${STK_SERVER_MODULES:=""}

exec_spacetime() {
  # args: target, environment, build root, modules
  echo "[*] Running configure command: 'python3 $SCRIPT_DIR/spacetime_py/spacetime.py $1 $2 $SCRIPT_DIR $SPACETIME_VERBOSE'"
  python3 "$SCRIPT_DIR/spacetime_py/spacetime.py" "$1" "$2" "$SCRIPT_DIR" "$SPACETIME_VERBOSE" "$4"
}
echo "[*] Running configure command 'spacetime $* $SCRIPT_DIR'"

exec_ninja() {
  # args: target
  echo "[*] Running build command: 'ninja -C $SCRIPT_DIR $1'"
  ninja -C "$SCRIPT_DIR" "$1"
}

sub_help() {
  echo "Spacetime - StarKingdoms build utility"
  echo "Spacetime is a small utility program to generate Ninja build manifests for compiling StarKingdoms."
  echo "Available targets:"
  echo "    help - Show this help screen" # done
  echo "    run_http - Compile the client and run a development http server for testing it" # done
  echo "    run_server (default) - Compile and run the game server" # done
  echo "    build_server - Compile the game server" # done
  echo "    run_server_prod - Compile and run the game server with optimizations enabled" # done
  echo "    build_server_prod - Compile the game server with optimizations enabled" # done
  echo "    run_api - Compile and run the API server" # done
  echo "    build_api - Compile the API server" # done
  echo "    run_api_prod - Compile and run the API server with optimizations enabled" # done
  echo "    build_api_prod - Compile the API server with optimizations enabled" # done
  echo "    install_tooling - Install the compilation utilities required for compiling StarKingdoms" # done
  echo "    build_assets - Compile spritesheets in all three texture sizes for textures-fast" # done
  echo "    build_assets_full - Compile spritesheets in full size for textures-fast" # done
  echo "    build_assets_375 - Commpile 37.5% spritesheets for textures-fast" # done
  echo "    build_assets_125 - Compile 12.5% spritesheets for textures-fast" # done
  echo "    clean - Remove all generated files" # done
  echo "    build_docker_api - Build the API dockerfile" # done
  echo "    build_docker_server - Build the server dockerfile" # done
  echo "    build_docker_web - Build the web dockerfile" # done
  echo "    build_docker - Build the API, web and server containers" # done
  echo "    build_docker_api_stable - Build the API container and push it as api-stable" # done
  echo "    build_docker_server_stable - Build the server container and push it as server-stable" # done
  echo "    build_docker_web_stable - Build the web dockerfile and push it as web-stable" # done
  echo "    build_docker_stable - Build the stable api, web and server containers" # done
  echo "    infra [action] - Run an infrastructure command. Requires an infrastructure key" # done
  echo "    client_protobuf - Rebuild the client protocol bindings" # done
}

check_install_cargo() {
  echo "[*] Checking for $1"
  if ! command -v "$1" &> /dev/null
  then
    echo "[+] $1 was not found, installing via Cargo..."
    cargo install "$2" $3
  fi
}

check() {
  echo "[*] Checking for $1"
  if ! command -v "$1" &> /dev/null
  then
    echo "[x] $1 was not found but is required for the build process to continue. Install it with your system package manager, or, if supported, 'st install_tooling'"
    exit 1
  fi
}

check_all() {
  check inkscape
  check protoc
  check atlasify
}

sub_clean() {
  rm -rf web/dist
  rm -rf assets/dist
  rm -rf assets/final
  rm -rf target
  rm -rf client/pkg
}

sub_install_tooling() {
  check inkscape
  check protoc
  check atlasify
  echo "[*] All required tools are installed"
}

sub_run_http() {
  check_all
  exec_spacetime client prod "$SCRIPT_DIR" "$CLIENT_MODS"
  exec_ninja asset
  cd client && yarn && yarn run dev
}

sub_client_protobuf() {
  cd client && yarn && yarn protobuf
}

sub_build_server() {
  check_all
  exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
}
sub_run_server() {
  check_all
  exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
  exec "$SCRIPT_DIR/target/debug/starkingdoms-server"
}

sub_build_server_prod() {
  check_all
  exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
}
sub_run_server_prod() {
  check_all
  exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
  exec "$SCRIPT_DIR/target/release/starkingdoms-server"
}

sub_build_api() {
  check_all
  exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
}
sub_run_api() {
  check_all
  exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
  cd api && exec "$SCRIPT_DIR/target/debug/starkingdoms-api"
}

sub_build_api_prod() {
  check_all
  exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
}
sub_run_api_prod() {
  check_all
  exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
  cd api && exec "$SCRIPT_DIR/target/release/starkingdoms-api"
}

sub_build_assets() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset
}

sub_build_assets_full() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset-full
}

sub_build_assets_375() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset-375
}

sub_build_assets_125() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset-125
}


build_docker() {
  docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD) "$SCRIPT_DIR"
  docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2" "$SCRIPT_DIR"
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD)
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2"
}

swap_out_server_for() {
  echo "[*] Swapping out API server"
  sed -i'orig' "s/let api_server = \"http:\\/\\/localhost:8080\";/let api_server = \"https:\\/\\/api.${1}.${2}\";/" "$SCRIPT_DIR/client/index.html"
  echo "[*] Swapping out game server"
  sed -i "s/let servers = \[\"localhost:3000\"\];/let servers = [\"${1}.${2}\"];/" "$SCRIPT_DIR/client/index.html"
}

sub_swap_server() {
  swap_out_server_for "$1" "$2"
}
sub_reset_server() {
  mv client/index.htmlorig client/index.html
}

sub_build_docker_api() {
  sub_build_api_prod
  build_docker "api" "bleeding"
}

sub_build_docker_server() {
  sub_build_server_prod
  build_docker "server" "bleeding"
}

sub_build_docker_web() {
  swap_out_server_for "bleeding" "starkingdoms.io"
  build_docker "web" "bleeding"
  mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
}

sub_build_docker_web_stable() {
  swap_out_server_for "starkingdoms" "io"
  build_docker "web" "stable"
  mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
}

sub_build_docker_api_stable() {
  sub_build_api_prod
  build_docker "api" "stable"
}

sub_build_docker_server_stable() {
  sub_build_server_prod
  build_docker "server" "stable"
}


sub_build_docker_api_beta() {
  sub_build_api_prod
  build_docker "api" "beta"
}

sub_build_docker_server_beta() {
  sub_build_server_prod
  build_docker "server" "beta"
}

sub_build_docker_web_beta() {
  swap_out_server_for "beta" "starkingdoms.io"
  build_docker "web" "beta"
  mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
}

sub_build_docker() {
  sub_build_docker_api
  sub_build_docker_server
  sub_build_docker_web
}

sub_build_docker_beta() {
  sub_build_docker_api_beta
  sub_build_docker_server_beta
  sub_build_docker_web_beta
}

sub_build_docker_stable() {
  sub_build_docker_api_stable
  sub_build_docker_server_stable
  sub_build_docker_web_stable
}

sub_infra() {
  echo "[*] Connecting to infrastructure manager server. If you are prompted for a password, enter your infrastructure key. You may be prompted several times."
  ssh team@10.16.1.3 /home/team/run_ansible.sh "$1"
}

subcommand=$1
case $subcommand in
    "" | "-h" | "--help" | "help")
        sub_help
        ;;
    *)
        echo "[*] Running build command $subcommand"
        shift
        sub_${subcommand} $@
        if [ $? = 127 ]; then
            echo "Error: '$subcommand' is not a known subcommand." >&2
            echo "       Run 'st --help' for a list of known subcommands." >&2
            exit 1
        fi
        ;;
esac
\ No newline at end of file
cd "$SCRIPT_DIR" && cargo run --release --bin spacetime -- "$@" "$SCRIPT_DIR"
\ No newline at end of file

A spacetime_old => spacetime_old +284 -0
@@ 0,0 1,284 @@
#!/bin/bash

set -e

SCRIPT_PATH=$(readlink -f "${BASH_SOURCE:-$0}")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
SERVER_MODS=${STK_SERVER_MODULES:=""}

exec_spacetime() {
  # args: target, environment, build root, modules
  echo "[*] Running configure command: 'python3 $SCRIPT_DIR/spacetime_py/spacetime.py $1 $2 $SCRIPT_DIR $SPACETIME_VERBOSE'"
  python3 "$SCRIPT_DIR/spacetime_py/spacetime.py" "$1" "$2" "$SCRIPT_DIR" "$SPACETIME_VERBOSE" "$4"
}

exec_ninja() {
  # args: target
  echo "[*] Running build command: 'ninja -C $SCRIPT_DIR $1'"
  ninja -C "$SCRIPT_DIR" "$1"
}

sub_help() {
  echo "Spacetime - StarKingdoms build utility"
  echo "Spacetime is a small utility program to generate Ninja build manifests for compiling StarKingdoms."
  echo "Available targets:"
  echo "    help - Show this help screen" # done
  echo "    run_http - Compile the client and run a development http server for testing it" # done
  echo "    run_server (default) - Compile and run the game server" # done
  echo "    build_server - Compile the game server" # done
  echo "    run_server_prod - Compile and run the game server with optimizations enabled" # done
  echo "    build_server_prod - Compile the game server with optimizations enabled" # done
  echo "    run_api - Compile and run the API server" # done
  echo "    build_api - Compile the API server" # done
  echo "    run_api_prod - Compile and run the API server with optimizations enabled" # done
  echo "    build_api_prod - Compile the API server with optimizations enabled" # done
  echo "    install_tooling - Install the compilation utilities required for compiling StarKingdoms" # done
  echo "    build_assets - Compile spritesheets in all three texture sizes for textures-fast" # done
  echo "    build_assets_full - Compile spritesheets in full size for textures-fast" # done
  echo "    build_assets_375 - Commpile 37.5% spritesheets for textures-fast" # done
  echo "    build_assets_125 - Compile 12.5% spritesheets for textures-fast" # done
  echo "    clean - Remove all generated files" # done
  echo "    build_docker_api - Build the API dockerfile" # done
  echo "    build_docker_server - Build the server dockerfile" # done
  echo "    build_docker_web - Build the web dockerfile" # done
  echo "    build_docker - Build the API, web and server containers" # done
  echo "    build_docker_api_stable - Build the API container and push it as api-stable" # done
  echo "    build_docker_server_stable - Build the server container and push it as server-stable" # done
  echo "    build_docker_web_stable - Build the web dockerfile and push it as web-stable" # done
  echo "    build_docker_stable - Build the stable api, web and server containers" # done
  echo "    infra [action] - Run an infrastructure command. Requires an infrastructure key" # done
  echo "    client_protobuf - Rebuild the client protocol bindings" # done
}

check_install_cargo() {
  echo "[*] Checking for $1"
  if ! command -v "$1" &> /dev/null
  then
    echo "[+] $1 was not found, installing via Cargo..."
    cargo install "$2" $3
  fi
}

check() {
  echo "[*] Checking for $1"
  if ! command -v "$1" &> /dev/null
  then
    echo "[x] $1 was not found but is required for the build process to continue. Install it with your system package manager, or, if supported, 'st install_tooling'"
    exit 1
  fi
}

check_all() {
  check inkscape
  check protoc
  check atlasify
}

sub_clean() {
  rm -rf web/dist
  rm -rf assets/dist
  rm -rf assets/final
  rm -rf target
  rm -rf client/pkg
}

sub_install_tooling() {
  check inkscape
  check protoc
  check atlasify
  echo "[*] All required tools are installed"
}

sub_run_http() {
  check_all
  exec_spacetime client prod "$SCRIPT_DIR" "$CLIENT_MODS"
  exec_ninja asset
  cd client && yarn && yarn run dev
}

sub_client_protobuf() {
  cd client && yarn && yarn protobuf
}

sub_build_server() {
  check_all
  exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
}
sub_run_server() {
  check_all
  exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
  exec "$SCRIPT_DIR/target/debug/starkingdoms-server"
}

sub_build_server_prod() {
  check_all
  exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
}
sub_run_server_prod() {
  check_all
  exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja server
  exec "$SCRIPT_DIR/target/release/starkingdoms-server"
}

sub_build_api() {
  check_all
  exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
}
sub_run_api() {
  check_all
  exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
  cd api && exec "$SCRIPT_DIR/target/debug/starkingdoms-api"
}

sub_build_api_prod() {
  check_all
  exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
}
sub_run_api_prod() {
  check_all
  exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
  exec_ninja api
  cd api && exec "$SCRIPT_DIR/target/release/starkingdoms-api"
}

sub_build_assets() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset
}

sub_build_assets_full() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset-full
}

sub_build_assets_375() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset-375
}

sub_build_assets_125() {
  check_all
  exec_spacetime asset dev "$SCRIPT_DIR"
  exec_ninja asset-125
}


build_docker() {
  docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD) "$SCRIPT_DIR"
  docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2" "$SCRIPT_DIR"
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD)
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2"
}

swap_out_server_for() {
  echo "[*] Swapping out API server"
  sed -i'orig' "s/let api_server = \"http:\\/\\/localhost:8080\";/let api_server = \"https:\\/\\/api.${1}.${2}\";/" "$SCRIPT_DIR/client/index.html"
  echo "[*] Swapping out game server"
  sed -i "s/let servers = \[\"localhost:3000\"\];/let servers = [\"${1}.${2}\"];/" "$SCRIPT_DIR/client/index.html"
}

sub_swap_server() {
  swap_out_server_for "$1" "$2"
}
sub_reset_server() {
  mv client/index.htmlorig client/index.html
}

sub_build_docker_api() {
  sub_build_api_prod
  build_docker "api" "bleeding"
}

sub_build_docker_server() {
  sub_build_server_prod
  build_docker "server" "bleeding"
}

sub_build_docker_web() {
  swap_out_server_for "bleeding" "starkingdoms.io"
  build_docker "web" "bleeding"
  mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
}

sub_build_docker_web_stable() {
  swap_out_server_for "starkingdoms" "io"
  build_docker "web" "stable"
  mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
}

sub_build_docker_api_stable() {
  sub_build_api_prod
  build_docker "api" "stable"
}

sub_build_docker_server_stable() {
  sub_build_server_prod
  build_docker "server" "stable"
}


sub_build_docker_api_beta() {
  sub_build_api_prod
  build_docker "api" "beta"
}

sub_build_docker_server_beta() {
  sub_build_server_prod
  build_docker "server" "beta"
}

sub_build_docker_web_beta() {
  swap_out_server_for "beta" "starkingdoms.io"
  build_docker "web" "beta"
  mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
}

sub_build_docker() {
  sub_build_docker_api
  sub_build_docker_server
  sub_build_docker_web
}

sub_build_docker_beta() {
  sub_build_docker_api_beta
  sub_build_docker_server_beta
  sub_build_docker_web_beta
}

sub_build_docker_stable() {
  sub_build_docker_api_stable
  sub_build_docker_server_stable
  sub_build_docker_web_stable
}

sub_infra() {
  echo "[*] Connecting to infrastructure manager server. If you are prompted for a password, enter your infrastructure key. You may be prompted several times."
  ssh team@10.16.1.3 /home/team/run_ansible.sh "$1"
}

subcommand=$1
case $subcommand in
    "" | "-h" | "--help" | "help")
        sub_help
        ;;
    *)
        echo "[*] Running build command $subcommand"
        shift
        sub_${subcommand} $@
        if [ $? = 127 ]; then
            echo "Error: '$subcommand' is not a known subcommand." >&2
            echo "       Run 'st --help' for a list of known subcommands." >&2
            exit 1
        fi
        ;;
esac
\ No newline at end of file

A spacetime_rs/Cargo.toml => spacetime_rs/Cargo.toml +10 -0
@@ 0,0 1,10 @@
[package]
name = "spacetime"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tabwriter = "1.2.1"
which = "4.4.0"
\ No newline at end of file

A spacetime_rs/src/cmd.rs => spacetime_rs/src/cmd.rs +17 -0
@@ 0,0 1,17 @@
use which::which;

pub fn enforce_commands() {
    println!("[spacetime] checking for required tooling");
    _enforce_command("cargo");
    _enforce_command("ninja");
    _enforce_command("yarn");
    _enforce_command("inkscape");
    _enforce_command("atlasify");
    println!("[spacetime] all required tools present");
}

fn _enforce_command(cmd: &str) {
    if which(cmd).is_err() {
        eprintln!("[!] Unable to find required binary {}. Please install it to continue.", cmd);
    }
}
\ No newline at end of file

A spacetime_rs/src/commands/api.rs => spacetime_rs/src/commands/api.rs +49 -0
@@ 0,0 1,49 @@
use std::error::Error;
use std::path::PathBuf;
use crate::configure::create_writer;
use crate::configure::rust::configure_rust_target;
use crate::ninja::{exec, exec_ninja};

pub fn build_api(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("api", "dev", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["api".to_string()])?;

    Ok(())
}

pub fn build_api_prod(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("api", "prod", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["api".to_string()])?;

    Ok(())
}

pub fn run_api(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("api", "dev", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["api".to_string()])?;

    exec(root.join("target/debug/starkingdoms-api").to_str().unwrap(), &root, args)?;

    Ok(())
}

pub fn run_api_prod(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("api", "prod", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["api".to_string()])?;

    exec(root.join("target/release/starkingdoms-api").to_str().unwrap(), &root, args)?;

    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/commands/assets.rs => spacetime_rs/src/commands/assets.rs +23 -0
@@ 0,0 1,23 @@
use std::error::Error;
use std::path::PathBuf;
use std::time::SystemTime;
use crate::configure::asset::configure_assets;
use crate::configure::create_writer;
use crate::ninja::exec_ninja;

pub fn build_assets(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    let start = SystemTime::now();

    configure_assets(&mut config_file_writer, &root)?;

    let end = SystemTime::now();
    let duration = end.duration_since(start).unwrap();

    println!("[spacetime] configure completed in {} seconds", duration.as_secs_f32());

    exec_ninja(&root, vec!["asset".to_string()])?;

    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/commands/clean.rs => spacetime_rs/src/commands/clean.rs +11 -0
@@ 0,0 1,11 @@
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use crate::ninja::exec;

pub fn clean(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    exec("cargo", &root, vec!["clean".to_string()])?;
    fs::remove_dir_all(root.join("assets").join("dist"))?;
    fs::remove_dir_all(root.join("assets").join("final"))?;
    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/commands/client.rs => spacetime_rs/src/commands/client.rs +28 -0
@@ 0,0 1,28 @@
use std::error::Error;
use std::path::PathBuf;
use crate::configure::client::configure_client;
use crate::configure::create_writer;
use crate::ninja::{exec, exec_ninja};

pub fn run_http(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_client(&mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["asset".to_string()])?;

    exec("yarn", &root.join("client"), vec![])?;
    exec("yarn", &root.join("client"), vec!["run".to_string(), "dev".to_string()])?;

    Ok(())
}

pub fn client_protobuf(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    exec("yarn", &root.join("client"), vec![])?;
    exec("yarn", &root.join("client"), vec!["protobuf".to_string()])
}

pub fn build_client_prod(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    exec("yarn", &root.join("client"), vec![])?;
    exec("yarn", &root.join("client"), vec!["build".to_string()])
}
\ No newline at end of file

A spacetime_rs/src/commands/docker.rs => spacetime_rs/src/commands/docker.rs +50 -0
@@ 0,0 1,50 @@
use std::error::Error;
use std::path::PathBuf;
use std::process::Command;
use crate::commands::api::build_api_prod;
use crate::commands::client::build_client_prod;
use crate::commands::server::build_server_prod;
use crate::ninja::exec;

fn _build(img: &str, channel: &str, root: &PathBuf) -> Result<(), Box<dyn Error>> {
    // compile the various thingies
    if img == "server" {
        build_server_prod(vec![], root.clone())?;
    } else if img == "api" {
        build_api_prod(vec![], root.clone())?;
    } else {
        build_client_prod(vec![], root.clone())?
    }

    let git_commit_id = String::from_utf8(Command::new("git").args(&["rev-parse", "--short", "HEAD"]).current_dir(root).output().unwrap().stdout).unwrap().replace('\n', "");
    exec("docker", root, vec![
        "buildx", "build", "-f",
        root.join(format!("{}.Dockerfile", img)).to_str().unwrap(),
        "-t", &format!("registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}", img, git_commit_id), root.to_str().unwrap()].iter().map(|u| u.to_string()).collect())?;
    exec("docker", root, vec![
        "buildx", "build", "-f",
        root.join(format!("{}.Dockerfile", img)).to_str().unwrap(),
        "-t", &format!("registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}", img, channel), root.to_str().unwrap()].iter().map(|u| u.to_string()).collect())?;

    exec("docker", root, vec![
        "push", &format!("registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}", img, git_commit_id)].iter().map(|u| u.to_string()).collect())?;
    exec("docker", root, vec![
        "push", &format!("registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}", img, channel)].iter().map(|u| u.to_string()).collect())?;
    
    Ok(())
}

pub fn build_docker_api(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("api", "bleeding", &root) }
pub fn build_docker_server(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("server", "bleeding", &root) }
pub fn build_docker_web(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("web", "bleeding", &root) }
pub fn build_docker(_a: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { build_docker_api(_a.clone(), root.clone())?; build_docker_server(_a.clone(), root.clone())?; build_docker_web(_a, root.clone()) }

pub fn build_docker_api_beta(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("api", "beta", &root) }
pub fn build_docker_server_beta(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("server", "beta", &root) }
pub fn build_docker_web_beta(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("web", "beta", &root) }
pub fn build_docker_beta(_a: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { build_docker_api_beta(_a.clone(), root.clone())?; build_docker_server_beta(_a.clone(), root.clone())?; build_docker_web_beta(_a, root.clone()) }

pub fn build_docker_api_stable(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("api", "stable", &root) }
pub fn build_docker_server_stable(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("server", "stable", &root) }
pub fn build_docker_web_stable(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { _build("web", "stable", &root) }
pub fn build_docker_stable(_a: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> { build_docker_api_stable(_a.clone(), root.clone())?; build_docker_server_stable(_a.clone(), root.clone())?; build_docker_web_stable(_a, root.clone()) }
\ No newline at end of file

A spacetime_rs/src/commands/mod.rs => spacetime_rs/src/commands/mod.rs +6 -0
@@ 0,0 1,6 @@
pub mod client;
pub mod assets;
pub mod clean;
pub mod api;
pub mod server;
pub mod docker;
\ No newline at end of file

A spacetime_rs/src/commands/server.rs => spacetime_rs/src/commands/server.rs +49 -0
@@ 0,0 1,49 @@
use std::error::Error;
use std::path::PathBuf;
use crate::configure::create_writer;
use crate::configure::rust::configure_rust_target;
use crate::ninja::{exec, exec_ninja};

pub fn build_server(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("server", "dev", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["server".to_string()])?;

    Ok(())
}

pub fn build_server_prod(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("server", "prod", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["server".to_string()])?;

    Ok(())
}

pub fn run_server(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("server", "dev", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["server".to_string()])?;

    exec(root.join("target/debug/starkingdoms-server").to_str().unwrap(), &root, args)?;

    Ok(())
}

pub fn run_server_prod(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
    let mut config_file_writer = create_writer(&root)?;

    configure_rust_target("server", "prod", &mut config_file_writer, &root)?;

    exec_ninja(&root, vec!["server".to_string()])?;

    exec(root.join("target/release/starkingdoms-server").to_str().unwrap(), &root, args)?;

    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/config.rs => spacetime_rs/src/config.rs +4 -0
@@ 0,0 1,4 @@
pub const ASSET_DIR: &str = "assets/";
pub const ASSETS_DIST_SUBDIR: &str = "dist/";
pub const ASSETS_SRC_SUBDIR: &str = "src/";
pub const ASSETS_FINAL_SUBDIR: &str = "final/";
\ No newline at end of file

A spacetime_rs/src/configure/asset.rs => spacetime_rs/src/configure/asset.rs +112 -0
@@ 0,0 1,112 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::fs::File;
use std::path::PathBuf;
use crate::config::{ASSET_DIR, ASSETS_DIST_SUBDIR, ASSETS_FINAL_SUBDIR, ASSETS_SRC_SUBDIR};
use crate::ninja::NinjaWriter;

pub fn configure_assets(writer: &mut NinjaWriter<File>, root: &PathBuf) -> Result<(), Box<dyn Error>> {
    // scan for assets
    let asset_src_dir = root.join(ASSET_DIR).join(ASSETS_SRC_SUBDIR);

    let mut found_assets = vec![];

    let files_in_src_dir = fs::read_dir(asset_src_dir)?;
    for maybe_asset in files_in_src_dir {
        let maybe_asset = maybe_asset?;
        if maybe_asset.file_name().to_str().unwrap().ends_with(".svg") {
            found_assets.push(maybe_asset.path());
        }
    }

    println!("[spacetime] asset scan: found {} assets", found_assets.len());

    let default_asset_size = 512;
    let asset_overrides = HashMap::from([
        ("earth.ink.svg", 2048),
        ("moon.ink.svg", 2048)
    ]);

    // generate an inkscape rule for all required asset sizes
    let mut written_rules_for = vec![];

    gen_inkscape_rule(default_asset_size, writer, &mut written_rules_for)?;

    for size in asset_overrides.values() {
        gen_inkscape_rule(*size, writer, &mut written_rules_for)?;
    }

    println!("[spacetime] generated {} image conversion rules", written_rules_for.len() * 3);

    let mut files_375 = vec![];
    let mut files_125 = vec![];
    let mut files_full = vec![];

    for asset in &found_assets {
        gen_convert_rule(asset, root, writer, &mut files_375, &mut files_full, &mut files_125, asset_size(asset.to_str().unwrap(), &asset_overrides, default_asset_size))?;
    }

    println!("[spacetime] generated {} image conversion steps", files_full.len() + files_125.len() + files_375.len());

    gen_packer_rule(root, writer, &files_375, &files_full, &files_125)?;

    println!("[spacetime] generated asset build commands");

    Ok(())
}

fn gen_packer_rule(root: &PathBuf, writer: &mut NinjaWriter<File>, files_375: &Vec<PathBuf>, files_full: &Vec<PathBuf>, files_125: &Vec<PathBuf>) -> Result<(), Box<dyn Error>> {
    writer.rule("pack", &format!("cd {} && atlasify -m 4096,4096 -o $out $in && touch $out", root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).to_string_lossy()), None, None, None, Some("console"), None, None, None, None)?;

    writer.build(vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-full").to_str().unwrap().to_string()], "pack".to_string(), files_full.iter().map(|u| u.to_str().unwrap().to_string()).collect(), vec![], vec![], HashMap::new(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-full.json").to_str().unwrap().to_string(), root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-full.png").to_str().unwrap().to_string()], None, None)?;
    writer.build(vec!["asset-full".to_string()], "phony".to_string(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-full").to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    writer.build(vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-125").to_str().unwrap().to_string()], "pack".to_string(), files_125.iter().map(|u| u.to_str().unwrap().to_string()).collect(), vec![], vec![], HashMap::new(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-125.json").to_str().unwrap().to_string(), root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-125.png").to_str().unwrap().to_string()], None, None)?;
    writer.build(vec!["asset-125".to_string()], "phony".to_string(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-125").to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    writer.build(vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-375").to_str().unwrap().to_string()], "pack".to_string(), files_375.iter().map(|u| u.to_str().unwrap().to_string()).collect(), vec![], vec![], HashMap::new(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-375.json").to_str().unwrap().to_string(), root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-375.png").to_str().unwrap().to_string()], None, None)?;
    writer.build(vec!["asset-375".to_string()], "phony".to_string(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-375").to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    writer.build(vec!["asset".to_string()], "phony".to_string(), vec![root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-375").to_str().unwrap().to_string(), root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-full").to_str().unwrap().to_string(), root.join(ASSET_DIR).join(ASSETS_DIST_SUBDIR).join("spritesheet-125").to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;


    Ok(())
}

fn gen_convert_rule(asset: &PathBuf, root: &PathBuf, writer: &mut NinjaWriter<File>, files_375: &mut Vec<PathBuf>, files_full: &mut Vec<PathBuf>, files_125: &mut Vec<PathBuf>, size: i32) -> Result<(), Box<dyn Error>> {
    let out_full = root.join(ASSET_DIR).join(ASSETS_FINAL_SUBDIR).join("full/").join(asset.file_stem().unwrap().to_str().unwrap().to_string() + ".png");
    files_full.push(out_full.clone());
    let rule_full = format!("inkscape_{}_px_full", size);
    writer.build(vec![out_full.to_str().unwrap().to_string()], rule_full, vec![asset.to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    let out_375 = root.join(ASSET_DIR).join(ASSETS_FINAL_SUBDIR).join("375/").join(asset.file_stem().unwrap().to_str().unwrap().to_string() + ".png");
    files_375.push(out_375.clone());
    let rule_375 = format!("inkscape_{}_px_375", size);
    writer.build(vec![out_375.to_str().unwrap().to_string()], rule_375, vec![asset.to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    let out_125 = root.join(ASSET_DIR).join(ASSETS_FINAL_SUBDIR).join("125/").join(asset.file_stem().unwrap().to_str().unwrap().to_string() + ".png");
    files_125.push(out_125.clone());
    let rule_125 = format!("inkscape_{}_px_125", size);
    writer.build(vec![out_125.to_str().unwrap().to_string()], rule_125, vec![asset.to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    Ok(())
}

fn asset_size(asset: &str, overrides: &HashMap<&str, i32>, default: i32) -> i32 {
    *overrides.get(asset).unwrap_or(&default)
}

fn gen_inkscape_rule(size: i32, writer: &mut NinjaWriter<File>, written: &mut Vec<i32>) -> Result<(), Box<dyn Error>> {
    if written.contains(&size) {
        return Ok(())
    }

    writer.rule(&format!("inkscape_{}_px_full", size), &format!("inkscape -w {} -h {} $in -o $out", size, size), None, None, None, None, None, None, None, None)?;
    writer.rule(&format!("inkscape_{}_px_375", size), &format!("inkscape -w {} -h {} $in -o $out", (size as f64 * 0.375) as i32, (size as f64 * 0.375) as i32), None, None, None, None, None, None, None, None)?;
    writer.rule(&format!("inkscape_{}_px_125", size), &format!("inkscape -w {} -h {} $in -o $out", (size as f64 * 0.125) as i32, (size as f64 * 0.125) as i32), None, None, None, None, None, None, None, None)?;

    written.push(size);

    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/configure/client.rs => spacetime_rs/src/configure/client.rs +11 -0
@@ 0,0 1,11 @@
use std::error::Error;
use std::fs::File;
use std::path::PathBuf;
use crate::configure::asset::configure_assets;
use crate::ninja::NinjaWriter;

pub fn configure_client(writer: &mut NinjaWriter<File>, root: &PathBuf) -> Result<(), Box<dyn Error>> {
    configure_assets(writer, root)?;

    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/configure/mod.rs => spacetime_rs/src/configure/mod.rs +15 -0
@@ 0,0 1,15 @@
use std::fs::File;
use std::io;
use std::path::PathBuf;
use crate::ninja::NinjaWriter;

pub mod asset;
pub mod client;
pub mod rust;

pub fn create_writer(root: &PathBuf) -> Result<NinjaWriter<File>, io::Error> {
    let mut w = NinjaWriter::new(File::create(root.join("build.ninja"))?);
    w.comment("Generated by spacetime")?;
    w.comment("Do not edit manually")?;
    Ok(w)
}
\ No newline at end of file

A spacetime_rs/src/configure/rust.rs => spacetime_rs/src/configure/rust.rs +36 -0
@@ 0,0 1,36 @@
/*
def gen_rules_for_api(root, env, writer, modules):
    if env == 'dev':
        out_dir = 'debug'
        writer.rule('cargo-api', f'cargo build --bin starkingdoms-api --features "{modules}"',
                    depfile=f'{root}/target/debug/starkingdoms-api.d', pool='console')
    elif env == 'prod':
        out_dir = 'release'
        writer.rule('cargo-api', f'cargo build --bin starkingdoms-api --release --features "{modules}"',
                    depfile=f'{root}/target/release/starkingdoms-api.d', pool='console')

    writer.build([f'{root}/target/{out_dir}/starkingdoms-api'], 'cargo-api', ['server/Cargo.toml'])
    writer.build(['api'], 'phony', [f'{root}/target/{out_dir}/starkingdoms-api'])
 */

use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::path::PathBuf;
use crate::ninja::NinjaWriter;

pub fn configure_rust_target(rust_target: &str, rust_env: &str, writer: &mut NinjaWriter<File>, root: &PathBuf) -> Result<(), Box<dyn Error>> {
    let out_dir;
    if rust_env == "dev" {
        out_dir = "debug";
        writer.rule(&format!("cargo-{}", rust_target), &format!("cargo build --bin starkingdoms-{}", rust_target), None, Some(root.join("target/debug/").join(format!("starkingdoms-{}.d", rust_target)).to_str().unwrap()), None, Some("console"), None, None, None, None)?;
    } else {
        out_dir = "release";
        writer.rule(&format!("cargo-{}", rust_target), &format!("cargo build --bin starkingdoms-{} --release", rust_target), None, Some(root.join("target/release/").join(format!("starkingdoms-{}.d", rust_target)).to_str().unwrap()), None, Some("console"), None, None, None, None)?;
    }

    writer.build(vec![root.join(format!("target/{}/", out_dir)).join(format!("starkingdoms-{}", rust_target)).to_str().unwrap().to_string()], "cargo-api".to_string(), vec![root.join("server/Cargo.toml").to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;
    writer.build(vec![rust_target.to_string()], "phony".to_string(), vec![root.join(format!("target/{}/", out_dir)).join(format!("starkingdoms-{}", rust_target)).to_str().unwrap().to_string()], vec![], vec![], HashMap::new(), vec![], None, None)?;

    Ok(())
}
\ No newline at end of file

A spacetime_rs/src/main.rs => spacetime_rs/src/main.rs +128 -0
@@ 0,0 1,128 @@
use std::collections::HashMap;
use std::error::Error;
use std::io::{Write};
use std::path::{PathBuf};
use std::time::SystemTime;
use tabwriter::TabWriter;
use crate::cmd::enforce_commands;
use crate::commands::api::{build_api, build_api_prod, run_api, run_api_prod};
use crate::commands::assets::build_assets;
use crate::commands::clean::{clean};
use crate::commands::client::{build_client_prod, client_protobuf, run_http};
use crate::commands::docker::{build_docker, build_docker_api, build_docker_api_beta, build_docker_api_stable, build_docker_beta, build_docker_server, build_docker_server_beta, build_docker_server_stable, build_docker_stable, build_docker_web, build_docker_web_beta, build_docker_web_stable};
use crate::commands::server::{build_server, build_server_prod, run_server, run_server_prod};

pub mod commands;
pub mod ninja;
pub mod cmd;
pub mod configure;
pub mod config;

fn main() {
    let mut bcm = BuildCommandManager::new();

    bcm.register("run_http", Box::new(run_http), "Compile the client and then run a development HTTP server", false);
    bcm.register("build_assets", Box::new(build_assets), "Compile the asset source files into spritesheets", false);
    bcm.register("client_protobuf", Box::new(client_protobuf), "Update the client protocol bindings", false);
    bcm.register("clean", Box::new(clean), "Remove all compilation artifacts", false);

    bcm.register("build_api", Box::new(build_api), "Compile the API server", false);
    bcm.register("run_api", Box::new(run_api), "Run the API server", false);
    bcm.register("build_api_prod", Box::new(build_api_prod), "Compile the API server with optimizations", false);
    bcm.register("run_api_prod", Box::new(run_api_prod), "Run the API server with optimizations", false);

    bcm.register("build_server", Box::new(build_server), "Compile the game server", false);
    bcm.register("run_server", Box::new(run_server), "Run the game server", false);
    bcm.register("build_server_prod", Box::new(build_server_prod), "Compile the game server with optimizations", false);
    bcm.register("run_server_prod", Box::new(run_server_prod), "Run the game server with optimizations", false);

    bcm.register("build_docker_beta", Box::new(build_docker_beta), "Build all three docker images for the beta channel", false);
    bcm.register("build_docker_api_beta", Box::new(build_docker_api_beta), "Build the API docker image for the beta channel", false);
    bcm.register("build_docker_server_beta", Box::new(build_docker_server_beta), "Build the main docker image for the beta channel", false);
    bcm.register("build_docker_web_beta", Box::new(build_docker_web_beta), "Build the webserver docker image for the beta channel", false);

    bcm.register("build_docker_stable", Box::new(build_docker_stable), "Build all three docker images for the stable channel", false);
    bcm.register("build_docker_api_stable", Box::new(build_docker_api_stable), "Build the API docker image for the stable channel", false);
    bcm.register("build_docker_server_stable", Box::new(build_docker_server_stable), "Build the main docker image for the stable channel", false);
    bcm.register("build_docker_web_stable", Box::new(build_docker_web_stable), "Build the webserver docker image for the stable channel", false);

    bcm.register("build_docker", Box::new(build_docker), "Build all three docker images for the bleeding channel", false);
    bcm.register("build_docker_api", Box::new(build_docker_api), "Build the API docker image for the bleeding channel", false);
    bcm.register("build_docker_server", Box::new(build_docker_server), "Build the main docker image for the bleeding channel", false);
    bcm.register("build_docker_web", Box::new(build_docker_web), "Build the webserver docker image for the bleeding channel", false);

    bcm.register("build_client_prod", Box::new(build_client_prod), "Build the production-ready client bundle", false);

    let start = SystemTime::now();

    let args: Vec<String> = std::env::args().collect();
    match bcm.exec(args) {
        Ok(_) => (),
        Err(e) => {
            let end = SystemTime::now();
            let duration = end.duration_since(start).unwrap();

            println!("[spacetime] Done in {} seconds", duration.as_secs_f32());

            eprintln!("[!] Error executing build command: {}", e);
            std::process::exit(-1);
        }
    }

    let end = SystemTime::now();
    let duration = end.duration_since(start).unwrap();

    println!("[spacetime] Done in {} seconds", duration.as_secs_f32());
}

type BuildCommandCallback = Box<dyn Fn(Vec<String>, PathBuf) -> Result<(), Box<dyn Error>>>;
#[derive(Default)]
pub struct BuildCommandManager {
    commands: HashMap<String, (BuildCommandCallback, String, bool)>
}
impl BuildCommandManager {
    pub fn new() -> Self {
        Self {
            commands: HashMap::new()
        }
    }

    pub fn register(&mut self, name: &str, command: BuildCommandCallback, description: &str, hidden: bool) {
        self.commands.insert(name.to_string(), (command, description.to_string(), hidden));
    }

    pub fn help(&self) {
        println!("Spacetime - StarKingdoms build utility");
        println!("Spacetime is a small Rust utility to assist in compiling StarKingdoms.");
        println!("Available targets:");

        let mut tw = TabWriter::new(vec![]);

        for (target, (_, description, hidden)) in &self.commands {
            if *hidden { continue };

            writeln!(tw, "\t{}\t{}", target, description).unwrap();
        }

        std::io::stdout().write_all(&tw.into_inner().unwrap()).unwrap();
    }

    pub fn exec(&self, args: Vec<String>) -> Result<(), Box<dyn Error>> {
        if args.len() < 2 || args[1] == "help" || args[1] == "h" {
            self.help();
            return Ok(())
        }

        enforce_commands();

        let main_path = PathBuf::from(args[2].to_string());

        if let Some((callback, _, _)) = self.commands.get(&args[1]) {
            callback(args[2..].to_owned(), main_path)
        } else {
            eprintln!("Unrecognized build command {}", args[1]);
            self.help();
            Ok(())
        }
    }
}
\ No newline at end of file

A spacetime_rs/src/ninja.rs => spacetime_rs/src/ninja.rs +157 -0
@@ 0,0 1,157 @@
use std::collections::HashMap;
use std::error::Error;
use std::io::Write;
use std::io;
use std::path::PathBuf;
use std::process::{Command, Stdio};

fn escape_path(word: &str) -> String {
    word.replace("$ ", "$$ ").replace(' ', "$ ").replace(':', "$:")
}

pub struct NinjaWriter<T: Write> {
    output: T
}
impl<T: Write> NinjaWriter<T> {
    pub fn new(output: T) -> Self {
        Self {
            output
        }
    }

    pub fn newline(&mut self) -> Result<(), io::Error> {
        writeln!(self.output)
    }

    pub fn comment(&mut self, text: &str) -> Result<(), io::Error> {
        writeln!(self.output, "# {}", text)
    }

    pub fn variable(&mut self, key: &str, value: &str, indent: usize) -> Result<(), io::Error> {
        if value.is_empty() {
            return Ok(())
        }
        writeln!(self.output, "{}{} = {}", self._indent(indent), key, value)
    }

    pub fn pool(&mut self, name: &str, depth: usize) -> Result<(), io::Error> {
        writeln!(self.output, "pool {}", name)?;
        self.variable("depth", &depth.to_string(), 1)
    }

    pub fn rule(&mut self, name: &str, command: &str, description: Option<&str>, depfile: Option<&str>, generator: Option<bool>, pool: Option<&str>, restat: Option<bool>, rspfile: Option<&str>, rspfile_content: Option<&str>, deps: Option<&str>) -> Result<(), io::Error> {
        writeln!(self.output, "rule {}", name)?;
        self.variable("command", command, 1)?;
        if let Some(desc) = description {
            self.variable("description", desc, 1)?;
        }
        if let Some(depfile) = depfile {
            self.variable("depfile", depfile, 1)?;
        }
        if let Some(gen) = generator {
            if gen {
                self.variable("generator", "1", 1)?;
            }
        }
        if let Some(pool) = pool {
            self.variable("pool", pool, 1)?;
        }
        if let Some(restat) = restat {
            if restat {
                self.variable("restat", "1", 1)?;
            }
        }
        if let Some(rspfile) = rspfile {
            self.variable("rspfile", rspfile, 1)?;
        }
        if let Some(rspfile_content) = rspfile_content {
            self.variable("rspfile_content", rspfile_content, 1)?;
        }
        if let Some(deps) = deps {
            self.variable("deps", deps, 1)?;
        }

        Ok(())
    }

    pub fn build(&mut self, outputs: Vec<String>, rule: String, inputs: Vec<String>, mut implicit: Vec<String>, mut order_only: Vec<String>, variables: HashMap<String, String>, mut implicit_outputs: Vec<String>, pool: Option<String>, dyndep: Option<String>) -> Result<(), io::Error> {
        let mut out_outputs: Vec<String> = outputs.iter().map(|u| escape_path(u)).collect();
        let mut all_inputs: Vec<String> = inputs.iter().map(|u| escape_path(u)).collect();

        if !implicit.is_empty() {
            all_inputs.push("|".to_string());
            all_inputs.append(&mut implicit.iter_mut().map(|u| escape_path(u)).collect());
        }
        if !order_only.is_empty() {
            all_inputs.push("||".to_string());
            all_inputs.append(&mut order_only.iter_mut().map(|u| escape_path(u)).collect());
        }
        if !implicit_outputs.is_empty() {
            out_outputs.push("|".to_string());
            out_outputs.append(&mut implicit_outputs.iter_mut().map(|u| escape_path(u)).collect());
        }

        all_inputs.insert(0, rule);

        writeln!(self.output, "build {}: {}", out_outputs.join(" "), all_inputs.join(" "))?;

        if let Some(pool) = pool {
            self.variable("pool", &pool, 1)?;
        }
        if let Some(dyndep) = dyndep {
            self.variable("dyndep", &dyndep, 1)?;
        }

        for (key, value) in variables {
            self.variable(&key, &value, 1)?;
        }

        Ok(())
    }

    pub fn include(&mut self, path: &str) -> Result<(), io::Error> {
        writeln!(self.output, "include {}", path)
    }

    pub fn subninja(&mut self, path: &str) -> Result<(), io::Error> {
        writeln!(self.output, "subninja {}", path)
    }

    pub fn default(&mut self, paths: Vec<String>) -> Result<(), io::Error> {
        writeln!(self.output, "default {}", paths.join(" "))
    }

    fn _indent(&self, num: usize) -> String {
        "  ".repeat(num)
    }
}

pub fn exec_ninja(root: &PathBuf, args: Vec<String>) -> Result<(), Box<dyn Error>> {
    let stat = Command::new("ninja")
        .args(args)
        .current_dir(root)
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()?
        .wait()?;
    if stat.success() {
        Ok(())
    } else {
        Err(format!("ninja exited with error: {}", stat.code().unwrap()).into())
    }
}

pub fn exec(cmd: &str, dir: &PathBuf, args: Vec<String>) -> Result<(), Box<dyn Error>> {
    let stat = Command::new(cmd)
        .args(args)
        .current_dir(dir)
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()?
        .wait()?;
    if stat.success() {
        Ok(())
    } else {
        Err(format!("{} exited with error: {}", cmd, stat.code().unwrap()).into())
    }
}
\ No newline at end of file