~starkingdoms/starkingdoms

459e397588120fcd0a08caa09d1312ab1bf9b0b8 — c0repwn3r 2 years ago 2f12fa8
infra overhaul
A ansible/deploy.yaml => ansible/deploy.yaml +55 -0
@@ 0,0 1,55 @@
- name: Deploy bleeding servers
  hosts: starkingdoms_prod_servers_bleeding
  vars:
    api_token: "{{ lookup('community.general.random_string', length=24) }}"
    jwt_signing_secret: "{{ lookup('community.general.random_string', length=24) }}"
    db_user: starkingdoms-bleeding
    db_pass: "{{ lookup('community.general.random_string', length=24) }}"
    db_name: starkingdoms-bleeding
    ws_port: 3000
    version: bleeding
    api_port: 8080
    api_config_dir: /home/stk-deploy/config
    db_data_dir: /home/stk-deploy/data
    compose_dir: /home/stk-deploy
    api_url: https://api.bleeding.starkingdoms.tk
    game_url: https://bleeding.starkingdoms.tk
    web_port: 8000
  tasks:
    - name: Ensure host connectivity
      ansible.builtin.ping:
    - name: Create configuration directory
      ansible.builtin.file:
        path: {{ api_config_dir }}
        state: directory
        mode: 0755
    - name: Create data directory
      ansible.builtin.file:
        path: {{ db_data_dir }}
        state: directory
        mode: 0755
    - name: Create API config file
      ansible.builtin.template:
        src: ../docker/config.jinja.toml
        dest: "{{ api_config_dir }}/config.toml"
    - name: Create docker-compose config file
      ansible.builtin.template:
        src: ../docker/docker-compose.jinja.yml
        dest: "{{ compose_dir }}/docker-compose.yml"
    - name: Start the server
      ansible.builtin.shell:
        cmd: docker-compose up -d
        chdir: "{{ compose_dir }}"


- name: Deploy beta servers
  hosts: starkingdoms_prod_servers_beta
  tasks:
    - name: Ensure host connectivity
      ansible.builtin.ping:

- name: Deploy stable servers
  hosts: starkingdoms_prod_servers_stable
  tasks:
    - name: Ensure host connectivity
      ansible.builtin.ping:
\ No newline at end of file

A ansible/inventory.yaml => ansible/inventory.yaml +23 -0
@@ 0,0 1,23 @@
starkingdoms_prod_servers_stable:
  hosts:
    ghosteast1:
      ansible_host: 10.17.4.2
      ansible_user: stk-deploy

starkingdoms_prod_servers_beta:
  hosts:
    east3:
      ansible_host: 10.16.2.5
      ansible_user: stk-deploy

starkingdoms_prod_servers_bleeding:
  hosts:
    central1:
      ansible_host: 10.16.1.1
      ansible_user: stk-deploy

starkingdoms_prod_servers:
  children:
    starkingdoms_prod_servers_stable:
    starkingdoms_prod_servers_beta:
    starkingdoms_prod_servers_bleeding:
\ No newline at end of file

M client/index.html => client/index.html +103 -77
@@ 1,80 1,106 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
    <meta charset="utf-8" />
    <title>StarKingdoms.TK</title>
    <link rel="stylesheet" href="./static/index.css"/>
</head>

<body>
<h1>StarKingdoms</h1>
<fieldset class="joingamebox">
    <legend>Join Game</legend>
    <form action="/play.html" method="GET">
        <label for="server">Choose server</label>
        <select class="joingamecontent" name="server" id="server">
            <!-- Dynamically filled by the JS later in this file -->
        </select>
        <br>
        <label for="textures">Texture quality</label>
        <select class="joingamecontent" name="textures" id="textures">
            <option value="full">Full quality (May impact loading times)</option>
            <option selected value="375">Medium quality (Recommended)</option>
            <option value="125">Low quality</option>
        </select>
        <br>
        <label for="username">Username</label>
        <br>
        <input class="m-5px" type="text" name="username" id="username" required />
        <br>
        <button class="m-5px w-full">Launch!</button>
        <br>
        <p id="loginstatus">You are not logged in.</p>
        <button style="display: none;" id="logout">Log out</button>
        <a href="http://localhost:8080/select-realm" id="login">Click here to log in or change accounts.</a>
    </form>
</fieldset>

<script>
    let api_server = "http://localhost:8080";
    let servers = ["localhost:3000"];

    function server_url_to_ping_url(server) { return "http://" + server + "/ping" }
    function server_url_to_gateway_url(server) { return "ws://" + server + "/ws" }

    function load_server(server) {
        // ping the server to get server information
        fetch(server_url_to_ping_url(server)).then(response => {
            response.json().then(response => {
                let elem = document.createElement("option");
                elem.value = server_url_to_gateway_url(server);
                elem.text = `${response.description} - ${response.version.name} - ${response.players} online`;
                document.getElementById("server").appendChild(elem);
            })
        })
    }

    for (let i = 0; i < servers.length; i++) {
        load_server(servers[i]);
    }

    let query = new URLSearchParams(window.location.search);

    if (query.has("token") && query.has("user")) {
        window.localStorage.setItem("token", query.get("token"));
        window.localStorage.setItem("user", query.get("user"));
    }

    if (window.localStorage.getItem("token") !== null && window.localStorage.getItem("user") !== null) {
        document.getElementById("logout").style.setProperty("display", "block");
        document.getElementById("logout").addEventListener("click", () => {
            window.localStorage.clear();
            window.location.reload();
        })
        document.getElementById("loginstatus").innerText = `Logged in! (you are ${window.localStorage.getItem("user")})`;
    }

    document.getElementById("login").href = `${api_server}/select-realm`;
</script>
</body>
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>StarKingdoms</title>
    </head>

    <body>
        <h1>StarKingdoms</h1>
        <fieldset class="joingamebox">
            <legend>Join Game</legend>
            <form action="/play.html" method="GET">
                <label for="server">Choose server</label>
                <select class="joingamecontent" name="server" id="server">
                    <!-- Dynamically filled by the JS later in this file -->
                </select>
                <br>
                <label for="textures">Texture quality</label>
                <select class="joingamecontent" name="textures" id="textures">
                    <option value="full">Full quality (May impact loading times)</option>
                    <option selected value="375">Medium quality (Recommended)</option>
                    <option value="125">Low quality</option>
                </select>
                <br>
                <label for="username">Username</label>
                <br>
                <input class="m-5px" type="text" name="username" id="username" required/>
                <br>
                <button class="m-5px w-full">Launch!</button>
                <br>
                <p id="loginstatus">You are not logged in.</p>
                <button style="display: none;" id="logout">Log out</button>
                <a href="http://localhost:8080/select-realm" id="login">Click here to log in or change accounts.</a>
            </form>
        </fieldset>


        <script type="module">
            let api_server = "http://localhost:8080";
            let servers = ["localhost:3000"];

            function server_url_to_ping_url(server) {
                return "http://" + server + "/ping"
            }

            function server_url_to_gateway_url(server) {
                return "ws://" + server + "/ws"
            }

            function load_server(server) {
                // ping the server to get server information
                fetch(server_url_to_ping_url(server)).then(response => {
                    response.json().then(response => {
                        let elem = document.createElement("option");
                        elem.value = server_url_to_gateway_url(server);
                        elem.text = `${response.description} - ${response.version.name} - ${response.players} online`;
                        document.getElementById("server").appendChild(elem);
                    })
                })
            }

            for (let i = 0; i < servers.length; i++) {
                load_server(servers[i]);
            }

            let query = new URLSearchParams(window.location.search);

            if (query.has("token") && query.has("user")) {
                window.localStorage.setItem("token", query.get("token"));
                window.localStorage.setItem("user", query.get("user"));
            }

            if (window.localStorage.getItem("token") !== null && window.localStorage.getItem("user") !== null) {
                document.getElementById("logout").style.setProperty("display", "block");
                document.getElementById("logout").addEventListener("click", () => {
                    window.localStorage.clear();
                    window.location.reload();
                })
                document.getElementById("loginstatus").innerText = `Logged in! (you are ${window.localStorage.getItem("user")})`;
            }

            document.getElementById("login").href = `${api_server}/select-realm`;
        </script>
    </body>

    <style>
        .joingamebox {
            width: min-content;
            padding: 10px;
        }

        .m-5px {
            margin: 5px;
        }

        .w-full {
            width: 100%;
        }

        .w-90 {
            width: 90%;
        }

    </style>
</html>

M client/package.json => client/package.json +2 -1
@@ 14,5 14,6 @@
    "typescript": "^4.9.3",
    "vite": "^4.2.0"
  },
  "dependencies": {}
  "dependencies": {},
  "entry": ["index.html", "play.html"]
}

M client/play.html => client/play.html +28 -1
@@ 4,7 4,6 @@
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>StarKingdoms</title>
    <link rel="stylesheet" href="./static/play.css" />
  </head>
  <body>
    <script type="module" src="/src/index.ts"></script>


@@ 18,4 17,32 @@
      <button style="display: none;" id="beamout">Beam out!</button>
    </div>
  </body>

  <style>
    :root {
      --ui-bg-color: gray;
      --ui-padding: 5px;
    }


    .renderbox {
      position: absolute;
      top: 0;
      left: 0;
    }

    body {
      margin: 0;
      padding: 0;
    }

    .infobox {
      position: absolute;
      bottom: 0;
      left: calc(50vw - 25%);
      width: 50%;
      background-color: var(--ui-bg-color);
      padding: var(--ui-padding);
    }
  </style>
</html>

M client/src/gateway.ts => client/src/gateway.ts +3 -1
@@ 31,11 31,13 @@ export interface GatewayClient {
    socket: WebSocket;
    username: string | null;
    version: number | null;
    // @ts-ignore
    ping_timeout: Timeout | null; // i am aware that these types dont exist
    // @ts-ignore
    ping_timeout_left: Timeout; // its fine
}

export async function gateway_connect(gateway_url: string, username: string): GatewayClient {
export async function gateway_connect(gateway_url: string, username: string): Promise<GatewayClient> {
    logger.info("FAST CONNECT - Connecting to gateway socket at " + gateway_url);

    let ws = await _websocket_connect(gateway_url);

M client/src/index.ts => client/src/index.ts +4 -8
@@ 151,11 151,7 @@ async function client_main(server: string, username: string, texture_quality: st
        global.client?.socket.send(encode(MessageC2SInput_packetInfo.type, msg));
    }

    let last_time = performance.now();
    let render = (now_time: DOMHighResTimeStamp) => {
        const delta_ms = now_time - last_time;
        last_time = now_time;

    let render = () => {
        let viewer_size_x = global.canvas.width;
        let viewer_size_y = global.canvas.height;



@@ 169,9 165,9 @@ async function client_main(server: string, username: string, texture_quality: st
        global.context.translate(viewer_size_x / 2, viewer_size_y / 2);

        if (global.me !== null) {
            document.getElementById("pos").innerText = `Position: ${Math.trunc(global.me.x)}, ${Math.trunc(global.me.y)}`;
            document.getElementById("pos")!.innerText = `Position: ${Math.trunc(global.me.x)}, ${Math.trunc(global.me.y)}`;
        }
        document.getElementById("vel").innerText = `Velocity: ${Math.trunc(global.velocity)}`;
        document.getElementById("vel")!.innerText = `Velocity: ${Math.trunc(global.velocity)}`;

        for (let i = 0; i < global.planets.length; i++) {
            let planet = global.planets[i];


@@ 196,7 192,7 @@ async function client_main(server: string, username: string, texture_quality: st
                    global.context.lineTo(planet.x - global.me!.x, planet.y - global.me!.y);
                    global.context.stroke();

                    document.getElementById("pos-moon").innerText = `Relative to Moon: ${Math.trunc(global.me!.x - planet.x)}, ${Math.trunc(global.me!.y - planet.y)}`
                    document.getElementById("pos-moon")!.innerText = `Relative to Moon: ${Math.trunc(global.me!.x - planet.x)}, ${Math.trunc(global.me!.y - planet.y)}`
                }
            } else if (planet.planetType == PlanetType.Earth) {
                if (global.me !== null) {

M client/src/protocol/message_c2s.ts => client/src/protocol/message_c2s.ts +2 -1
@@ 1,4 1,5 @@
/* eslint-disable */
// @ts-nocheck

import * as _m0 from "protobufjs/minimal";
import { GoodbyeReason, goodbyeReasonFromJSON, goodbyeReasonToJSON } from "./goodbye_reason";
import { State, stateFromJSON, stateToJSON } from "./state";

M client/src/protocol/starkingdoms-protocol.ts => client/src/protocol/starkingdoms-protocol.ts +1 -1
@@ 1,4 1,4 @@
/* eslint-disable */
// @ts-nocheck
import * as Long from "long";
import * as _m0 from "protobufjs/minimal";


D client/static/index.css => client/static/index.css +0 -16
@@ 1,16 0,0 @@
.joingamebox {
    width: min-content;
    padding: 10px;
}

.m-5px {
    margin: 5px;
}

.w-full {
    width: 100%;
}

.w-90 {
    width: 90%;
}

D client/static/play.css => client/static/play.css +0 -21
@@ 1,21 0,0 @@
@import "root.css";

.renderbox {
    position: absolute;
    top: 0;
    left: 0;
}

body {
    margin: 0;
    padding: 0;
}

.infobox {
    position: absolute;
    bottom: 0;
    left: calc(50vw - 25%);
    width: 50%;
    background-color: var(--ui-bg-color);
    padding: var(--ui-padding);
}
\ No newline at end of file

D client/static/root.css => client/static/root.css +0 -4
@@ 1,4 0,0 @@
:root {
    --ui-bg-color: gray;
    --ui-padding: 5px;
}
\ No newline at end of file

A client/vite.config.ts => client/vite.config.ts +12 -0
@@ 0,0 1,12 @@
import { defineConfig } from "vite";

export default defineConfig({
    build: {
        lib: {
            entry: {
                play: "play.html",
                index: "index.html",
            }
        },
    },
});

M docker/docker-compose.jinja.yml => docker/docker-compose.jinja.yml +4 -0
@@ 19,6 19,10 @@ services:
      - {{ api_port }}:8080
    volumes:
      - {{ api_config_dir }}:/etc/starkingdoms
  web:
    image: registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:web-{{ version }}
    ports:
      - {{ web_port }}:80
  postgres:
    # docker run --name basic-postgres --rm -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=4y7sV96vA9wv46VR -e PGDATA=/var/lib/postgresql/data/pgdata -v /tmp:/var/lib/postgresql/data -p 5432:5432 -it postgres:14.1-alpine
    image: postgres:14.1-alpine

A docker/mime-types.conf => docker/mime-types.conf +4 -0
@@ 0,0 1,4 @@
include mime.types;
types {
    application/javascript js mjs cjs;
}
\ No newline at end of file

M spacetime => spacetime +58 -44
@@ 40,10 40,12 @@ sub_help() {
  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 - Build the API and server containers" # 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_stable - Build the stable api and server containers" # 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
}

check_install_cargo() {


@@ 70,48 72,6 @@ check_all() {
  check atlasify
}

sub_build_docker_api() {
  cargo build --release --bin starkingdoms-api
  docker buildx build -f api.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-$(git rev-parse --short HEAD) .
  docker buildx build -f api.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-bleeding .
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-$(git rev-parse --short HEAD)
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-bleeding
}

sub_build_docker_server() {
  cargo build --release --bin starkingdoms-server
  docker buildx build -f server.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-$(git rev-parse --short HEAD) .
  docker buildx build -f server.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-bleeding .
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-$(git rev-parse --short HEAD)
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-bleeding
}

sub_build_docker_api_stable() {
  cargo build --release --bin starkingdoms-api
  docker buildx build -f api.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-$(git rev-parse --short HEAD) .
  docker buildx build -f api.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-stable .
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-$(git rev-parse --short HEAD)
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:api-stable
}

sub_build_docker_server_stable() {
  cargo build --release --bin starkingdoms-server
  docker buildx build -f server.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-$(git rev-parse --short HEAD) .
  docker buildx build -f server.Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-stable .
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-$(git rev-parse --short HEAD)
  docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:server-stable
}

sub_build_docker() {
  sub_build_docker_api
  sub_build_docker_server
}

sub_build_docker_stable() {
  sub_build_docker_api_stable
  sub_build_docker_server_stable
}

sub_clean() {
  rm -rf web/dist
  rm -rf assets/dist


@@ 206,6 166,60 @@ sub_build_assets_125() {
  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"

}

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

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

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 = [\"https:\\/\\/$1.$2\"];/" "$SCRIPT_DIR/client/index.html"
}

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_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() {
  sub_build_docker_api
  sub_build_docker_server
  sub_build_docker_web
}

sub_build_docker_stable() {
  sub_build_docker_api_stable
  sub_build_docker_server_stable
  sub_build_docker_web_stable
}

subcommand=$1
case $subcommand in
    "" | "-h" | "--help" | "help")

A web.Dockerfile => web.Dockerfile +14 -0
@@ 0,0 1,14 @@
FROM node

RUN npm i -g parcel

COPY client /client

RUN cd /client && yarn
RUN cd /client && yarn build

FROM nginx

COPY --from=0 /client/dist /usr/share/nginx/html
COPY assets/dist /usr/share/nginx/html
COPY docker/mime-types.conf /etc/nginx/conf.d/mime-types.conf
\ No newline at end of file