aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-06-04 20:48:43 +0200
committermetamuffin <metamuffin@disroot.org>2024-06-04 20:48:43 +0200
commit4ebe819106d82459def54561cf8dc71ec22ba6e4 (patch)
tree991046e078bf9de21cc392343401ef1d46a8962c
parente49a85505701740b195a03892e1fc5cf8d6382a2 (diff)
downloadgpn-tron-rust-4ebe819106d82459def54561cf8dc71ec22ba6e4.tar
gpn-tron-rust-4ebe819106d82459def54561cf8dc71ec22ba6e4.tar.bz2
gpn-tron-rust-4ebe819106d82459def54561cf8dc71ec22ba6e4.tar.zst
save creds
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--config.toml3
-rw-r--r--src/config.rs3
-rw-r--r--src/database.rs33
-rw-r--r--src/game/server.rs41
-rw-r--r--src/lib.rs10
-rw-r--r--src/main.rs3
-rw-r--r--src/spectate/index.html35
-rw-r--r--src/spectate/main.ts3
-rw-r--r--src/spectate/server.rs21
-rw-r--r--src/spectate/style.css43
12 files changed, 182 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9225731..b072c6c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -436,6 +436,7 @@ dependencies = [
"log",
"mime",
"rand 0.9.0-alpha.1",
+ "redb",
"serde",
"serde_json",
"tokio",
@@ -828,6 +829,15 @@ dependencies = [
]
[[package]]
+name = "redb"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed7508e692a49b6b2290b56540384ccae9b1fb4d77065640b165835b56ffe3bb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 1c6fdd2..b2b72f0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,3 +17,4 @@ serde_json = "1.0.117"
headers = "0.4.0"
mime = "0.3.17"
rand = "0.9.0-alpha.1"
+redb = "2.1.0"
diff --git a/config.toml b/config.toml
index 6eef0ca..e9a72af 100644
--- a/config.toml
+++ b/config.toml
@@ -9,3 +9,6 @@ tickrate_max = 3
[bot]
amount = 10
+
+[database]
+path = "/tmp/trondb"
diff --git a/src/config.rs b/src/config.rs
index eea7991..b489271 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,10 +1,11 @@
-use crate::{bot, game, spectate};
+use crate::{bot, database, game, spectate};
use anyhow::{anyhow, Result};
use serde::Deserialize;
use std::fs::read_to_string;
#[derive(Deserialize)]
pub struct Config {
+ pub database: database::Config,
pub game: game::Config,
pub spectate: spectate::Config,
pub bot: bot::Config,
diff --git a/src/database.rs b/src/database.rs
new file mode 100644
index 0000000..ac67857
--- /dev/null
+++ b/src/database.rs
@@ -0,0 +1,33 @@
+use anyhow::Result;
+use redb::{Database, ReadableTable, TableDefinition};
+use serde::Deserialize;
+use std::path::PathBuf;
+
+#[derive(Deserialize)]
+pub struct Config {
+ path: PathBuf,
+}
+
+pub fn open_db(config: Config) -> Result<Database> {
+ let db = Database::create(config.path)?;
+
+ Ok(db)
+}
+
+static T_CREDS: TableDefinition<&str, &str> = TableDefinition::new("creds");
+
+pub trait DatabaseExt {
+ fn check_or_insert_creds(&self, username: &str, password: &str) -> anyhow::Result<bool>;
+}
+
+impl DatabaseExt for Database {
+ fn check_or_insert_creds(&self, username: &str, password: &str) -> anyhow::Result<bool> {
+ let txn = self.begin_write()?;
+ let mut table = txn.open_table(T_CREDS)?;
+ if let Some(pw) = table.get(username)? {
+ return Ok(pw.value() == password);
+ }
+ table.insert(username, password)?;
+ Ok(true)
+ }
+}
diff --git a/src/game/server.rs b/src/game/server.rs
index f5de1bf..0e983b9 100644
--- a/src/game/server.rs
+++ b/src/game/server.rs
@@ -1,5 +1,5 @@
use super::{protocol::Packet, Config, Game};
-use crate::State;
+use crate::{database::DatabaseExt, State};
use anyhow::{anyhow, bail, Result};
use log::{debug, error, info};
use std::{ops::ControlFlow, sync::Arc, time::Duration};
@@ -29,7 +29,7 @@ pub async fn game_server(config: Config, state: Arc<State>) -> Result<()> {
}
async fn game_loop(config: Config, state: Arc<State>) {
- let mut speed = config.tickrate;
+ let mut speed = config.tickrate;
loop {
sleep(Duration::from_secs_f32(1. / speed)).await;
@@ -43,9 +43,18 @@ async fn game_loop(config: Config, state: Arc<State>) {
}
ControlFlow::Break(winner) => {
info!("winner: {winner:?}");
+ if let Some(winner) = winner {
+ if let Some(winner) = state.players.write().await.get(&winner).cloned() {
+ let mut h = state.win_history.write().await;
+ h.push_front(winner);
+ while h.len() > 64 {
+ h.pop_back();
+ }
+ }
+ }
let p = state.players.read().await;
*g = Game::new(p.clone().into_iter().collect());
- speed = config.tickrate;
+ speed = config.tickrate;
let _ = state.tick.send(true);
}
}
@@ -81,7 +90,7 @@ async fn handle_client_inner(
let mut lines = rx.lines();
let mut ticks = state.tick.subscribe();
let mut chat = state.chat.subscribe();
- tx.send_packet(Packet::Motd("This is the GPN-Tron Rust rewrite. The protocol should be compatible with the original: https://github.com/freehuntx/gpn-tron/blob/master/PROTOCOL.md".to_string())).await?;
+ tx.send_packet(Packet::Motd("This is the GPN-Tron Rust rewrite. It should be compatible with the original protocol: https://github.com/freehuntx/gpn-tron/blob/master/PROTOCOL.md".to_string())).await?;
loop {
tokio::select! {
message = chat.recv() => {
@@ -127,6 +136,9 @@ async fn handle_tick(
}
cstate.alive = true;
}
+ if !cstate.alive {
+ return Ok(());
+ }
{
let g = state.game.read().await;
if new_game {
@@ -153,9 +165,11 @@ async fn handle_tick(
events.push(Packet::Die(g.dead.clone()));
}
if g.dead.contains(&pid) {
+ cstate.alive = false;
events.push(Packet::Lose(0, 0)); // TODO implement stats
+ } else {
+ events.push(Packet::Tick);
}
- events.push(Packet::Tick);
}
for e in events {
tx.send_packet(e).await?;
@@ -179,10 +193,19 @@ async fn handle_packet(
};
debug!("<- {packet:?}");
match packet {
- Packet::Join {
- username,
- password: _,
- } => {
+ Packet::Join { username, password } => {
+ if username.len() > 64 || password.len() > 64 {
+ tx.send_packet(Packet::Error(
+ "password or username too long (> 64)".to_string(),
+ ))
+ .await?;
+ return Ok(());
+ }
+ if !state.db.check_or_insert_creds(&username, &password)? {
+ tx.send_packet(Packet::Error("incorrect password".to_string()))
+ .await?;
+ return Ok(());
+ }
if cstate.pid.is_some() {
tx.send_packet(Packet::Error("already joined".to_string()))
.await?
diff --git a/src/lib.rs b/src/lib.rs
index b3f62fe..757fb13 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,17 +1,21 @@
#![feature(async_closure)]
#![feature(iterator_try_collect)]
-use std::collections::HashMap;
-
use game::Game;
+use redb::Database;
+use std::collections::{HashMap, VecDeque};
use tokio::sync::{broadcast, RwLock};
+
+pub mod bot;
pub mod config;
+pub mod database;
pub mod game;
pub mod spectate;
-pub mod bot;
pub struct State {
pub tick: broadcast::Sender<bool>, // true for new game
pub game: RwLock<Game>,
pub players: RwLock<HashMap<u32, String>>,
+ pub win_history: RwLock<VecDeque<String>>,
pub chat: broadcast::Sender<(String, String)>,
+ pub db: Database,
}
diff --git a/src/main.rs b/src/main.rs
index 74d784b..fb0f0d2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
use gpn_tron2::{
bot::spawn_bots,
config::Config,
+ database::open_db,
game::{server::game_server, Game},
spectate::server::spectate_server,
State,
@@ -13,9 +14,11 @@ async fn main() -> anyhow::Result<()> {
env_logger::init_from_env("LOG");
let config = Config::load()?;
let state = Arc::new(State {
+ db: open_db(config.database)?,
tick: broadcast::channel(512).0,
game: Game::new(vec![]).into(),
players: Default::default(),
+ win_history: Default::default(),
chat: broadcast::channel(512).0,
});
spawn(spectate_server(config.spectate, state.clone()));
diff --git a/src/spectate/index.html b/src/spectate/index.html
index 394b780..5b9dc5a 100644
--- a/src/spectate/index.html
+++ b/src/spectate/index.html
@@ -12,5 +12,40 @@
This is the GPN-Tron spectator application. You need JavaScript to
run it.
</noscript>
+ <div id="side">
+ <div id="info">
+ <h1>GPN-Tron</h1>
+ <p>
+ This is the GPN-Tron Rust rewrite. It should be compatible
+ with the original. Connect via TCP and join the fun!
+ </p>
+ <ul>
+ <li>
+ Spectator:
+ <script>
+ document.write(`HTTP ${window.location.host}`);
+ </script>
+ </li>
+ <li>
+ Player:
+ <script>
+ document.write(
+ `TCP ${window.location.hostname}:4000`
+ );
+ </script>
+ </li>
+ </ul>
+ <a href="https://codeberg.org/metamuffin/gpn-tron-rust">
+ Source code
+ </a>
+ </div>
+ <div id="scoreboard">
+ <h2>Scoreboard</h2>
+ </div>
+ <div id="chat">
+ <h2>Chat</h2>
+ </div>
+ </div>
+ <div id="board"></div>
</body>
</html>
diff --git a/src/spectate/main.ts b/src/spectate/main.ts
index ea84924..508ca30 100644
--- a/src/spectate/main.ts
+++ b/src/spectate/main.ts
@@ -35,7 +35,7 @@ let ctx: CanvasRenderingContext2D
document.addEventListener("DOMContentLoaded", () => {
canvas = document.createElement("canvas")
ctx = canvas.getContext("2d")!
- document.body.append(canvas)
+ document.getElementById("board")?.append(canvas)
canvas.width = 1000;
canvas.height = 1000;
redraw()
@@ -122,7 +122,6 @@ function name_color(name: string): string {
ws.onerror = console.error
ws.onmessage = message => {
const p = JSON.parse(message.data) as Packet
- console.log(p);
if (p == "tick") {
tick_anim = 0
const d = []
diff --git a/src/spectate/server.rs b/src/spectate/server.rs
index da18a77..11c4d5a 100644
--- a/src/spectate/server.rs
+++ b/src/spectate/server.rs
@@ -47,6 +47,19 @@ pub async fn spectate_server(config: Config, state: Arc<State>) -> Result<()> {
Ok(())
}
+#[cfg(debug_assertions)]
+async fn index() -> Html<String> {
+ use tokio::fs::read_to_string;
+ Html(
+ read_to_string(concat!(
+ env!("CARGO_MANIFEST_DIR"),
+ "/src/spectate/index.html"
+ ))
+ .await
+ .unwrap(),
+ )
+}
+#[cfg(not(debug_assertions))]
async fn index() -> Html<&'static str> {
Html(include_str!("index.html"))
}
@@ -91,13 +104,7 @@ async fn css() -> (HeaderMap, &'static str) {
use headers::HeaderMapExt;
let mut hm = HeaderMap::new();
hm.typed_insert(ContentType::from_str("text/css").unwrap());
- (
- hm,
- include_str!(concat!(
- env!("CARGO_MANIFEST_DIR"),
- "/src/spectate/style.css"
- )),
- )
+ (hm, include_str!("style.css"))
}
async fn broadcaster(sstate: Arc<SpectateState>, state: Arc<State>) {
diff --git a/src/spectate/style.css b/src/spectate/style.css
index 52711d0..c391020 100644
--- a/src/spectate/style.css
+++ b/src/spectate/style.css
@@ -1,4 +1,43 @@
+* {
+ color: white;
+}
+body {
+ padding: 0px;
+ margin: 0px;
+ background-color: black;
+
+ width: 100dvw;
+ height: 100dvh;
+ display: flex;
+ flex-direction: row;
+ overflow-y: hidden;
+}
+
canvas {
- height: min(100dvh, 70dvw);
- float: left;
+ object-fit: contain;
+ width: 100%;
+ height: 100%;
+}
+#board {
+ width: 60%;
+ height: 100%;
+}
+#side {
+ width: 30%;
+ height: 100%;
+}
+#info {
+ width: 100%;
+ height: 30%;
+ overflow-y: scroll;
+}
+#scoreboard {
+ width: 100%;
+ height: 30%;
+ overflow-y: scroll;
+}
+#chat {
+ width: 100%;
+ height: 40%;
+ overflow-y: scroll;
}