Skip to content

Commit

Permalink
Merge branch 'main' into backend
Browse files Browse the repository at this point in the history
  • Loading branch information
ettolrach authored Mar 2, 2024
2 parents 99894df + 2032457 commit d1a37a9
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 19 deletions.
62 changes: 62 additions & 0 deletions .github/workflows/build-game-client.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: "Build CosmicKube game client"
on:
push:
branches:
- main

env:
GODOT_VERSION: 4.2.1
EXPORT_NAME: CosmicKube
PROJECT_PATH: game-source

jobs:
export-web:
name: "Export for Web"
runs-on: ubuntu-20.04
container:
image: barichello/godot-ci:4.2.1
steps:
- name: Checkout
uses: actions/checkout@v4
with:
lfs: true
- name: Setup
run: |
mkdir -v -p ~/.local/share/godot/export_templates/
mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
- name: Web Build
run: |
mkdir -v -p build/web
cd $PROJECT_PATH
godot --headless --verbose --export-release "Web" ../build/web/index.html 2>&1 | tee output.txt
echo Reading build logs...
if search="$(cat output.txt | grep 'ERROR: Project export')"
then
echo "Build failed!"
exit 1
else
echo "Build succeeded!"
exit 0
fi ;
- name: Create staticwebapp.config.json
run: |
cd build/web
echo "${{ vars.STATIC_WEB_APP_CONFIG }}" > staticwebapp.config.json
ls
- name: Upload Artifact
uses: actions/upload-artifact@v1
with:
name: web
path: build/web

- name: Publish to Azure Static Web Apps
id: publishto
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for GitHub integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations ######
app_location: "build/web"
skip_app_build: true
###### End of Repository/Build Configurations ######
18 changes: 18 additions & 0 deletions .github/workflows/test-jenkinsfile.yml.noworky
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: "Test Jenkinsfile"
on: push

env:
GODOT_VERSION: 4.2.1
EXPORT_NAME: CosmicKube
PROJECT_PATH: game-source

jobs:
test-jenkins:
name: "Test Jenkinsfile"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@master
- name: jenkinsfile-runner-prepackaged
uses: jenkinsci/jenkinsfile-runner-github-actions/jenkinsfile-runner-prepackaged@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 changes: 31 additions & 17 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
pipeline {
agent any
agent {
docker {
image 'barichello/godot-ci:4.2.1'

} }
environment {
GODOT_VERSION = '4.2.1'
EXPORT_NAME = 'CosmicKube'
PROJECT_PATH = 'game-source'
}

stages {
stage('Hello world') {
stage('Setup') {
steps {
sh 'echo pee pee'
sh '''mkdir -v -p ~/.local/share/godot/export_templates/
mv /root/.local/share/godot/export_templates/${GODOT_VERSION}.stable ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable
'''
}
}
stage('Web Build') {
steps {
sh '''mkdir -v -p build/web
cd $PROJECT_PATH
godot --headless --verbose --export-release "Web" ../build/web/index.html 2>&1 | tee output.txt
echo Reading build logs...
if search="$(cat output.txt | grep 'ERROR: Project export')"
then
echo "Build failed!"
exit 1
else
echo "Build succeeded!"
exit 0
fi ;'''
}
}

// stage('Push image') {
// steps {
// sh 'docker push localhost:5000/ttt'
// }
// }
//
// stage('Package') {
// steps {
// sh 'helm install ttt ttt | true'
// sh 'helm upgrade ttt ttt'
// }
// }
}
}
}
5 changes: 5 additions & 0 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ diesel = { version = "2.1.0", features = ["postgres"] }
dotenvy = "0.15"
uuid = { version = "1.7.0", features = ["v5", "fast-rng", "macro-diagnostics"] }
tokio = { version = "1", features = ["full"] }
tokio-stream = "0.1.6"
async-trait = "0.1.77"
warp = "0.3"
serde = { version = "1.0", features = ["derive"]}
serde_json = "1.0"
futures = { version = "0.3", default-features=false}
9 changes: 9 additions & 0 deletions backend/src/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::{ws, Clients, Result};
use warp::Reply;

pub async fn ws_handler(ws: warp::ws::Ws, clients: Clients) -> Result<impl Reply>
{
println!("ws_handler"); //debug

Ok(ws.on_upgrade(move |socket| ws::client_connection(socket, clients)))
}
2 changes: 2 additions & 0 deletions backend/src/kube.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ impl Kube {
}
}
}

// we should have a placeholder ''loading'' cube we can send over if api is slow
40 changes: 38 additions & 2 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
fn main() {
println!("Hello, world!");
use std::{collections::HashMap, convert::Infallible, sync::Arc};
use tokio::sync::{mpsc, Mutex};
use warp::{filters::ws::Message, Filter, Rejection};

mod handlers;
mod ws;

// type that represents a connecting client
#[derive(Debug, Clone)]
pub struct Client {
pub client_id: String,
pub sender: Option<mpsc::UnboundedSender<std::result::Result<Message, warp::Error>>>,
}

// type aliases!
type Clients = Arc<Mutex<HashMap<String, Client>>>;
type Result<T> = std::result::Result<T, Rejection>;

#[tokio::main]
async fn main() {

//initialise a hashmap to store currently connected clients. We may want some more logic here if we want currently connected clients to be stored somewhere
let clients: Clients = Arc::new(Mutex::new(HashMap::new()));

println!("configuring websocket route"); //debug
let ws_route = warp::path("ws")
.and(warp::ws())
.and(with_clients(clients.clone()))
.and_then(handlers::ws_handler);

let routes = ws_route.with(warp::cors().allow_any_origin());
println!("starting server"); //debug
warp::serve(routes).run(([127, 0, 0, 1], 8000)).await;
}

fn with_clients(clients: Clients) -> impl Filter<Extract = (Clients,), Error = Infallible> + Clone {
warp::any().map(move || clients.clone())
}

15 changes: 15 additions & 0 deletions backend/src/websocketstructs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

// these are the structs intended for use when communicating via websockets

// this is the data we expect to recieve from the player
pub struct PlayerInfo {
player: Player, //the player requesting the data
coordinates: [u64; 2], //current player coordinates
action: String, //PLACEHOLDER! we need to know what the player is doing.
}

// this is the data we expect to send to the player
pub struct GameState {
grid: String, //PLACEHOLDER! This will be the partial grid state type
}

81 changes: 81 additions & 0 deletions backend/src/ws.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use crate::{Client, Clients};
use futures::{FutureExt, StreamExt};
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
use uuid::Uuid;
use warp::ws::{Message, WebSocket};

pub async fn client_connection(ws: WebSocket, clients: Clients) {
println!("establishing client connection... {:?}", ws); //debug

// splitting the WebSocket stream object into separate 'Sink' and 'Stream' objects.
// This lets us split up the logic of sending and recieving tasks
// 'Stream' lets us recieve messages from the client
// 'Sink' letes us establish a connection from the unbounded channel
let (client_ws_sender, mut client_ws_rcv) = ws.split();
// creates an unbounded channel. It is configured to send messages to the client.
let (client_sender, client_rcv) = mpsc::unbounded_channel();

let client_rcv = UnboundedReceiverStream::new(client_rcv);

// 'spawns' a new task, that stays alive until the client has disconnected.
tokio::task::spawn(client_rcv.forward(client_ws_sender).map(|result| {
if let Err(e) = result {
println!("error sending websocket msg: {}", e);
}
}));

// creating a new uuid to use as the key in the 'clients' hashmap, and a new instance of a 'client'
let uuid = Uuid::new_v4().simple().to_string();

let new_client = Client {
client_id: uuid.clone(),
//the client_sender object is stored within this new client instance so that we can send messages to this connected client in other parts of the code
sender: Some(client_sender),
};

//obtains a lock on the client list and inserts the new client into the hashmap using the uuid as the key.
clients.lock().await.insert(uuid.clone(), new_client);

// creates a loop that handles incoming messages from the client
while let Some(result) = client_ws_rcv.next().await {
let msg = match result {
Ok(msg) => msg,
Err(e) => {
println!("error receiving message for id {}): {}", uuid.clone(), e);
break;
}
};
client_msg(&uuid, msg, &clients).await;
}

// as the above will keep running as long as the client is active, when we exit the loop we can safely remove this client instance from the hashmap.
clients.lock().await.remove(&uuid);
println!("{} disconnected", uuid); //debug
}

// example function to respond to a clients message, this just responds to 'ping!' with 'pong!', but later we will replace this with;
// ->recieve client game info <- send back client game state
// wwwwwwwwwwwwwwwwwwwww i am so tired
async fn client_msg(client_id: &str, msg: Message, clients: &Clients) {
println!("received message from {}: {:?}", client_id, msg); //debug

let message = match msg.to_str() {
Ok(v) => v,
Err(_) => return,
};

if message == "ping" || message == "ping\n" {
let locked = clients.lock().await;
match locked.get(client_id) {
Some(v) => {
if let Some(sender) = &v.sender {
println!("sending pong");
let _ = sender.send(Ok(Message::text("pong")));
}
}
None => return,
}
return;
};
}
58 changes: 58 additions & 0 deletions client.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
FROM ubuntu:jammy
LABEL author="https://github.com/aBARICHELLO/godot-ci/graphs/contributors"

USER root
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
git \
git-lfs \
unzip \
wget \
zip \
adb \
openjdk-17-jdk-headless \
rsync \
&& rm -rf /var/lib/apt/lists/*

ARG GODOT_VERSION="4.2.1"
ARG RELEASE_NAME="stable"
ARG SUBDIR=""
ARG GODOT_TEST_ARGS=""
ARG GODOT_PLATFORM="linux.x86_64"

RUN wget https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}${SUBDIR}/Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM}.zip \
&& wget https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}${SUBDIR}/Godot_v${GODOT_VERSION}-${RELEASE_NAME}_export_templates.tpz \
&& mkdir ~/.cache \
&& mkdir -p ~/.config/godot \
&& mkdir -p ~/.local/share/godot/export_templates/${GODOT_VERSION}.${RELEASE_NAME} \
&& unzip Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM}.zip \
&& mv Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM} /usr/local/bin/godot \
&& unzip Godot_v${GODOT_VERSION}-${RELEASE_NAME}_export_templates.tpz \
&& mv templates/* ~/.local/share/godot/export_templates/${GODOT_VERSION}.${RELEASE_NAME} \
&& rm -f Godot_v${GODOT_VERSION}-${RELEASE_NAME}_export_templates.tpz Godot_v${GODOT_VERSION}-${RELEASE_NAME}_${GODOT_PLATFORM}.zip

# Download and setup android-sdk
ENV ANDROID_HOME="/usr/lib/android-sdk"
RUN wget https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip \
&& unzip commandlinetools-linux-*_latest.zip -d cmdline-tools \
&& mv cmdline-tools $ANDROID_HOME/ \
&& rm -f commandlinetools-linux-*_latest.zip

ENV PATH="${ANDROID_HOME}/cmdline-tools/cmdline-tools/bin:${PATH}"

RUN yes | sdkmanager --licenses \
&& sdkmanager "platform-tools" "build-tools;33.0.2" "platforms;android-33" "cmdline-tools;latest" "cmake;3.22.1" "ndk;25.2.9519653"

# Adding android keystore and settings
RUN keytool -keyalg RSA -genkeypair -alias androiddebugkey -keypass android -keystore debug.keystore -storepass android -dname "CN=Android Debug,O=Android,C=US" -validity 9999 \
&& mv debug.keystore /root/debug.keystore

RUN godot -v -e --quit --headless ${GODOT_TEST_ARGS}
RUN echo 'export/android/android_sdk_path = "/usr/lib/android-sdk"' >> ~/.config/godot/editor_settings-4.tres
RUN echo 'export/android/debug_keystore = "/root/debug.keystore"' >> ~/.config/godot/editor_settings-4.tres
RUN echo 'export/android/debug_keystore_user = "androiddebugkey"' >> ~/.config/godot/editor_settings-4.tres
RUN echo 'export/android/debug_keystore_pass = "android"' >> ~/.config/godot/editor_settings-4.tres
RUN echo 'export/android/force_system_user = false' >> ~/.config/godot/editor_settings-4.tres
RUN echo 'export/android/timestamping_authority_url = ""' >> ~/.config/godot/editor_settings-4.tres
RUN echo 'export/android/shutdown_adb_on_exit = true' >> ~/.config/godot/editor_settings-4.tres
Loading

0 comments on commit d1a37a9

Please sign in to comment.