Skip to content

Commit

Permalink
feat: game config editor, monaco
Browse files Browse the repository at this point in the history
  • Loading branch information
shirok1 committed Feb 3, 2024
1 parent 2f867b5 commit 32d25fc
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 22 deletions.
Binary file modified frontend/bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions frontend/components/SideNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,9 @@ const links = [{
label: 'Saves Management',
icon: 'i-heroicons-archive-box',
to: '/saves'
}, {
label: 'Config Editor',
icon: 'i-heroicons-adjustments-horizontal',
to: '/editor'
}]
</script>
83 changes: 83 additions & 0 deletions frontend/components/editor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<div class="flex flex-col gap-4 h-dvh py-10 -my-10">
<div class="flex-none flex flex-row justify-end gap-2">
<UButton to="https://tech.palworldgame.com/optimize-game-balance" variant="link" label="Offical Guide" />
<UButton :disabled="!ready" @click="load('default')" color="white" label="Load Default" />
<UButton :disabled="!ready" @click="load('current')" color="red" label="Load Current" />
<UButton :disabled="!ready" @click="save" label="Save" />
</div>
<USkeleton v-if="!ready" class="grow" />
<div ref="containerRef" :class="ready ? 'grow' : ''" />
</div>
</template>

<script setup lang="ts">
import { onUnmounted } from 'vue'
import { useMonaco } from '@guolao/vue-monaco-editor'
import type { editor } from '@monaco-editor/loader/node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts'
const containerRef = ref()
const { monacoRef, unload } = useMonaco()
const editorRef = shallowRef<editor.IStandaloneCodeEditor>()
const ready = computed(() => editorRef.value !== undefined)
const defaultValue = "; Waiting for loading to complete..."
const stopTryLoading = watchEffect(async () => {
if (monacoRef.value && containerRef.value) {
nextTick(() => stopTryLoading())
editorRef.value = monacoRef.value.editor.create(containerRef.value, {
automaticLayout: true,
formatOnType: true,
formatOnPaste: true,
wordWrap: 'on',
theme: 'vs-dark',
language: 'ini',
readOnly: true,
value: defaultValue
})
const value = await $fetch<string>("/proxy/gateway/game_config/current").catch(() => {
toast.add({ title: "Loading default", description: "Since config file is not created yet" })
return $fetch<string>("/proxy/gateway/game_config/default")
}
)
editorRef.value?.setValue(value)
editorRef.value?.updateOptions({ readOnly: false })
}
})
onUnmounted(() => {
if (!monacoRef.value) {
unload()
} else {
editorRef.value?.dispose()
}
})
const toast = useToast()
const load = async (path: "default" | "current") => {
const value = await $fetch<string>(`/proxy/gateway/game_config/${path}`, {
onResponseError: async () => {
toast.add({ title: "Failed to load", description: "Maybe it is not created yet, try saving", color: "red", icon: 'i-heroicons-x-circle-20-solid' })
}
})
toast.add({ title: "Loaded", description: `Loaded ${path} config`, icon: 'i-heroicons-check-circle-20-solid' })
editorRef.value?.setValue(value)
editorRef.value?.updateOptions({ readOnly: false })
}
const save = async () => {
const value = editorRef.value?.getValue()
if (value) {
await $fetch<string>('/proxy/gateway/game_config/save', {
method: 'POST',
body: value,
onResponseError: async () => {
toast.add({ title: "Failed to save", description: "Check the console for more information", color: "red", icon: 'i-heroicons-x-circle-20-solid' })
}
})
toast.add({ title: "Saved", description: "Saved the config", icon: 'i-heroicons-check-circle-20-solid' })
}
}
</script>
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"@extractus/feed-extractor": "^7.0.9",
"@guolao/vue-monaco-editor": "^1.5.0",
"@iconify-json/mdi": "^1.1.64",
"@nuxt/ui": "^2.12.3",
"@pinia/nuxt": "^0.5.1",
Expand Down
5 changes: 5 additions & 0 deletions frontend/pages/editor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<ClientOnly>
<Editor />
</ClientOnly>
</template>
33 changes: 33 additions & 0 deletions gateway/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ thiserror = "1.0.56"
tokio = { version = "1.35.1", features = ["full"] }
tokio-stream = { version = "0.1.14", features = ["full"] }
tokio-util = { version = "0.7.10", features = ["full"] }
tower-http = { version = "0.5.1", features = ["trace"] }
tower-http = { version = "0.5.1", features = ["trace", "fs"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
42 changes: 42 additions & 0 deletions gateway/src/game_config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
mod unreal_struct;

pub mod route {
use std::path::Path;

use axum::{routing::post, Router};
use tower_http::services::ServeFile;

use crate::AppResult;

pub fn new_router(base_path: impl AsRef<Path>) -> Router<()> {
let base_path = base_path.as_ref();
let default_path = base_path.join("DefaultPalWorldSettings.ini");
let current_path = base_path.join("Pal/Saved/Config/LinuxServer/PalWorldSettings.ini");
Router::new()
.route_service("/default", ServeFile::new(&default_path))
.route_service("/current", ServeFile::new(&current_path))
.route("/save", post(|body| save(body, current_path)))
}

async fn save(body: String, path: impl AsRef<Path>) -> AppResult<()> {
tokio::fs::create_dir_all(path.as_ref().parent().unwrap()).await?;
tokio::fs::write(path, body).await?;
Ok(())
}
}

// fn parse_ini() {
// let i = Ini::load_from_file("/Users/shiroki/Downloads/DefaultPalWorldSettings.ini").unwrap();
// for (sec, prop) in i.iter() {
// println!("Section: {:?}", sec);
// for (k, v) in prop.iter() {
// // println!("{}:{}", k, v);
// if v.starts_with('(') && v.ends_with(')') {
// // assume as Unreal config struct
// println!("{k}: {:?}", unreal_struct::parse_struct(v));
// } else {
// println!("{k}: {v}");
// }
// }
// }
// }
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use pest::Parser;
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "unreal_struct.pest"]
#[grammar = "game_config/unreal_struct.pest"]
pub struct UnrealSturctParser;

#[derive(Debug)]
Expand Down
4 changes: 3 additions & 1 deletion gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ use thiserror::Error;
pub mod pal;
pub mod rcon;
pub mod steamcmd;
pub mod unreal_struct;
pub mod game_config;

#[derive(Error, Debug)]
enum AppError {
#[error("error from the inner RCON client")]
PalworldCommandError(#[from] pal::PalworldCommandError),
#[error("error during IO")]
IOError(#[from] std::io::Error),
}

impl IntoResponse for AppError {
Expand Down
22 changes: 3 additions & 19 deletions gateway/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
// fn main() {
// let i = Ini::load_from_file("/Users/shiroki/Downloads/DefaultPalWorldSettings.ini").unwrap();
// for (sec, prop) in i.iter() {
// println!("Section: {:?}", sec);
// for (k, v) in prop.iter() {
// // println!("{}:{}", k, v);
// if v.starts_with('(') && v.ends_with(')') {
// // assume as Unreal config struct
// println!("{k}: {:?}", gateway_rs::unreal_struct::parse_struct(v));
// } else {
// println!("{k}: {v}");
// }
// }
// }
// }

use axum::{routing::get, Router};
use palboard_gateway::{
pal::{self, PalServerClient},
steamcmd,
game_config, pal::{self, PalServerClient}, steamcmd
};
use std::env;
use tracing::{info, warn};
Expand All @@ -43,7 +26,8 @@ async fn main() {
let app = Router::new()
.route("/version", get(VERSION.unwrap_or("unknown")))
.nest("/pal", pal::route::new_router(client))
.nest("/steam", steamcmd::route::new_router());
.nest("/steam", steamcmd::route::new_router())
.nest("/game_config", game_config::route::new_router("/home/steam/palserver/"));

let listener = tokio::net::TcpListener::bind(env::var("GATEWAY_ADDR").unwrap_or_else(|_| {
warn!("you should set `GATEWAY_ADDR` environment variable, frontend will connect to this address");
Expand Down

0 comments on commit 32d25fc

Please sign in to comment.