diff --git a/Cargo.lock b/Cargo.lock index 167c461..2faa7af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + [[package]] name = "addr2line" version = "0.21.0" @@ -163,6 +169,20 @@ dependencies = [ "syn 2.0.43", ] +[[package]] +name = "async-tungstenite" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6acf7e4a267eecbb127ed696bb2d50572c22ba7f586a646321e1798d8336a1" +dependencies = [ + "futures-io", + "futures-util", + "log", + "pin-project-lite", + "tokio", + "tungstenite", +] + [[package]] name = "asynchronous-codec" version = "0.6.2" @@ -196,12 +216,53 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "axum" version = "0.7.2" @@ -209,7 +270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "202651474fe73c62d9e0a56c6133f7a0ff1dc1c8cf7a5b03381af2a26553ac9d" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.1", "bytes", "futures-util", "http 1.0.0", @@ -235,6 +296,23 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.11", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "axum-core" version = "0.4.1" @@ -260,13 +338,16 @@ name = "backend" version = "0.1.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.2", "chrono", "clap 4.4.11", + "colourado", "names", "petgraph 0.6.4", "prisma-client-rust", "rand 0.8.5", + "rand_chacha 0.3.1", + "rspc", "serde", "serde_json", "tokio", @@ -316,6 +397,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64" version = "0.21.5" @@ -661,6 +748,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "cmake" version = "0.1.50" @@ -686,6 +782,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "colourado" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51b47ec6e60f823a917da8b2e0b2261e3ca1976de32f94f7338687363b727e" +dependencies = [ + "rand 0.6.5", +] + [[package]] name = "combine" version = "3.8.1" @@ -736,6 +841,17 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -811,7 +927,7 @@ version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ - "autocfg", + "autocfg 1.1.0", "cfg-if", "crossbeam-utils", ] @@ -1112,6 +1228,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + [[package]] name = "dotenv" version = "0.15.0" @@ -1402,6 +1527,12 @@ dependencies = [ "syn 2.0.43", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "2.0.0" @@ -1761,6 +1892,25 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "httpz" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6769d2cb10425c442c41a787d1a3719744770a188cfa7885cd4d8126fa13f37a" +dependencies = [ + "async-tungstenite", + "axum 0.6.20", + "base64 0.20.0", + "cookie", + "form_urlencoded", + "futures", + "http 0.2.11", + "hyper 0.14.28", + "sha1", + "thiserror", + "tokio", +] + [[package]] name = "hyper" version = "0.14.28" @@ -1891,7 +2041,7 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg", + "autocfg 1.1.0", "hashbrown 0.12.3", "serde", ] @@ -2177,13 +2327,19 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ - "autocfg", + "autocfg 1.1.0", "scopeguard", ] @@ -2737,7 +2893,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ - "autocfg", + "autocfg 1.1.0", "num-integer", "num-traits", ] @@ -2757,7 +2913,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg", + "autocfg 1.1.0", "num-traits", ] @@ -2767,7 +2923,7 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg", + "autocfg 1.1.0", "num-integer", "num-traits", ] @@ -2778,7 +2934,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg", + "autocfg 1.1.0", "num-bigint", "num-integer", "num-traits", @@ -2790,7 +2946,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -3256,10 +3412,12 @@ dependencies = [ "psl", "query-connector", "query-core", + "rspc", "schema", "serde", "serde-value", "serde_json", + "specta", "thiserror", "user-facing-errors", "uuid", @@ -3293,6 +3451,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", + "specta", "syn 1.0.109", ] @@ -3613,6 +3772,25 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -3623,7 +3801,7 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", ] [[package]] @@ -3637,6 +3815,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -3657,6 +3845,21 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -3675,6 +3878,15 @@ dependencies = [ "getrandom 0.2.11", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3684,6 +3896,59 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "raw-cpuid" version = "10.7.0" @@ -3693,6 +3958,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -3873,6 +4147,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rspc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68477fb8c11280503aeab140c41b7a0eed57156a73185062ffb4782c18a918f" +dependencies = [ + "futures", + "httpz", + "serde", + "serde_json", + "specta", + "thiserror", + "tokio", +] + [[package]] name = "rusqlite" version = "0.25.4" @@ -4325,7 +4614,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -4354,6 +4643,39 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "specta" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2240c3aa020aa61d2c569087d213baafbb212f4ceb9de9dd162376ea6aa0fe3" +dependencies = [ + "chrono", + "document-features", + "indexmap 1.9.3", + "indoc", + "once_cell", + "paste", + "serde", + "serde_json", + "specta-macros", + "thiserror", + "uuid", +] + +[[package]] +name = "specta-macros" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4605306321c356e03873b8ee71d7592a5e7c508add325c3ed0677c16fdf1bcfb" +dependencies = [ + "Inflector", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", + "termcolor", +] + [[package]] name = "spin" version = "0.9.8" @@ -5076,6 +5398,25 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http 0.2.11", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -5200,6 +5541,12 @@ dependencies = [ "user-facing-error-macros", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index deb04d0..3cb17a5 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -10,10 +10,17 @@ anyhow = "1.0.76" axum = { version = "0.7.2", features = ["http2"] } chrono = "0.4.31" clap = { version = "4.4.11", features = ["derive"] } -prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust", tag = "0.6.10" } serde = { version = "1.0.193", features = ["derive"] } tokio = { version = "1.35.1", features = ["full"] } serde_json = "1.0.108" petgraph = "0.6.4" names = "0.14.0" rand = "0.8.5" +rspc = { version = "0.1.3", features = ["axum"] } +rand_chacha = "0.3.1" +colourado = "0.2.0" + +[dependencies.prisma-client-rust] +git = "https://github.com/Brendonovich/prisma-client-rust" +tag = "0.6.10" +features = ["rspc"] diff --git a/backend/src/generate/map.rs b/backend/src/generate/map.rs index ca11915..c06655f 100644 --- a/backend/src/generate/map.rs +++ b/backend/src/generate/map.rs @@ -4,13 +4,14 @@ use anyhow::Result; use petgraph::{ algo::min_spanning_tree, data::FromElements, graphmap::UnGraphMap, }; +use rand::SeedableRng; use triangle::{triangulate, Point, TrianglulateOpts}; use crate::state::{StarSystemCoreState, StarSystemId}; pub struct Map { pub star_systems: HashMap, - pub hyperlanes: UnGraphMap, + pub hyperlanes: UnGraphMap, } pub fn generate_map() -> Result { @@ -42,6 +43,7 @@ pub fn generate_map() -> Result { let star_system = StarSystemCoreState { id, position: (point.x, point.y), + owned_by_empire_id: None, }; star_systems.insert(id, star_system); } @@ -49,9 +51,21 @@ pub fn generate_map() -> Result { // Format the hyperlanes into petgraph let mut hyperlanes = UnGraphMap::default(); for indexes in result.triangle_list.into_iter() { - hyperlanes.add_edge(indexes[0], indexes[1], ()); - hyperlanes.add_edge(indexes[0], indexes[2], ()); - hyperlanes.add_edge(indexes[1], indexes[2], ()); + hyperlanes.add_edge( + StarSystemId(indexes[0] as i32), + StarSystemId(indexes[1] as i32), + (), + ); + hyperlanes.add_edge( + StarSystemId(indexes[0] as i32), + StarSystemId(indexes[2] as i32), + (), + ); + hyperlanes.add_edge( + StarSystemId(indexes[1] as i32), + StarSystemId(indexes[2] as i32), + (), + ); } // Let's just drop some random % of hyperlanes diff --git a/backend/src/generate/mod.rs b/backend/src/generate/mod.rs index 9756d53..5af1331 100644 --- a/backend/src/generate/mod.rs +++ b/backend/src/generate/mod.rs @@ -4,11 +4,17 @@ use std::collections::HashMap; use anyhow::Result; use chrono::Utc; +use colourado::{ColorPalette, PaletteType}; use names::Generator as NameGenerator; +use petgraph::algo::k_shortest_path; +use rand::{ + rngs::ThreadRng, seq::SliceRandom, thread_rng, RngCore, SeedableRng, +}; +use rand_chacha::ChaCha12Rng; use crate::{ prisma::PrismaClient, - state::{EmpireCoreState, EmpireId, GameCoreState, UniverseId}, + state::{EmpireCoreState, EmpireId, GameCoreState, StarSystemId, UniverseId}, }; use self::map::generate_map; @@ -16,20 +22,74 @@ use self::map::generate_map; pub async fn generate_initial_game_state( client: &PrismaClient, ) -> Result { + // Generate a seed + let mut seed = [0; 32]; + { + let mut rng = thread_rng(); + rng.fill_bytes(&mut seed); + } + let mut seeded_rng = ChaCha12Rng::from_seed(seed); + // Generate a new universe ID let universe = client.universe().create(json!({}), vec![]).exec().await?; + let mut map = generate_map()?; + + // TODO: take this in via some kind of world-gen options + let num_empires = 10; + + // Pick `num_empires` amount of starting systems. + let starting_points = { + let mut starting_points = Vec::::new(); + let mut star_system_ids = map.star_systems.keys().collect::>(); + star_system_ids.shuffle(&mut seeded_rng); + let mut iter = star_system_ids.iter(); + + // TODO: Is this slow? make this process faster than O(n^2 * whatever) + while starting_points.len() < num_empires { + let next_starting_point = **iter.next().unwrap(); + let distances = + k_shortest_path(&map.hyperlanes, next_starting_point, None, 1, |_| 1); + if starting_points + .iter() + .all(|starting_point| *distances.get(starting_point).unwrap() > 5) + { + starting_points.push(next_starting_point); + } + } + starting_points + }; + + // Generate color palettes for empires + let palette = + ColorPalette::new(num_empires as u32, PaletteType::Pastel, false); // Generate some empires let mut empires = HashMap::new(); let name_generator = NameGenerator::default(); - for (idx, name) in name_generator.take(10).enumerate() { + for (idx, ((name, capital_system_id), color)) in name_generator + .take(num_empires) + .zip(starting_points) + .zip(palette.colors.into_iter()) + .enumerate() + { let id = EmpireId(idx as i32); - let empire = EmpireCoreState { id, name }; + + // Set the star system's owner + map + .star_systems + .get_mut(&capital_system_id) + .unwrap() + .owned_by_empire_id = Some(id); + + let empire = EmpireCoreState { + id, + name, + color, + capital_system_id, + }; empires.insert(id, empire); } - let map = generate_map()?; - Ok(GameCoreState { universe_id: UniverseId(universe.id), current_instant: Utc::now(), diff --git a/backend/src/main.rs b/backend/src/main.rs index baded14..d7ebcfb 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -6,6 +6,7 @@ mod generate; pub mod prisma; mod routes; pub mod state; +mod utils; use std::sync::Arc; @@ -14,7 +15,7 @@ use axum::{routing::get, Router}; use clap::{Parser, Subcommand}; use prisma::PrismaClient; -use routes::{universe_list, universe_map}; +use routes::{empire_list, universe_list, universe_map}; use crate::generate::generate_initial_game_state; @@ -50,8 +51,10 @@ async fn main() -> Result<()> { let app = Router::new() .route("/", get(|| async { "Hello, World!" })) .route("/universe", get(universe_list)) + .route("/universe/:universe_id/empires", get(empire_list)) .route("/universe/:universe_id/map", get(universe_map)) .with_state(state); + let listener = tokio::net::TcpListener::bind("0.0.0.0:1440").await?; axum::serve(listener, app).await?; } @@ -59,7 +62,6 @@ async fn main() -> Result<()> { Command::Seed => { let game_state = generate_initial_game_state(&client).await?; game_state.save_to_database(&client).await?; - println!("Done."); } } diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs index f7fda66..14a553a 100644 --- a/backend/src/routes/mod.rs +++ b/backend/src/routes/mod.rs @@ -6,7 +6,7 @@ use prisma_client_rust::Direction; use serde_json::Value; use crate::{ - prisma::{star_system, star_system_edges}, + prisma::{empire, star_system, star_system_edges}, state::UniverseId, AppState, }; @@ -25,18 +25,33 @@ pub async fn universe_list(state: State) -> Json { })) } +pub async fn empire_list( + Path((universe_id,)): Path<(i32,)>, + state: State, +) -> Json { + let empires = state + .prisma + .empire() + .find_many(vec![empire::universe_id::equals(universe_id)]) + .exec() + .await + .unwrap(); + + Json(json!({ + "empires": empires, + })) +} + pub async fn universe_map( Path((universe_id,)): Path<(i32,)>, state: State, ) -> Json { - let universe_id = UniverseId(universe_id); - let points = state .prisma .star_system() - .find_many(vec![star_system::universe_id::equals(universe_id.0)]) + .find_many(vec![star_system::universe_id::equals(universe_id)]) .order_by(star_system::index::order(Direction::Asc)) - .select(star_system::select!({ index coord_x coord_y })) + .select(star_system::select!({ index coord_x coord_y owned_by_empire_id })) .exec() .await .unwrap(); @@ -44,7 +59,7 @@ pub async fn universe_map( let edges = state .prisma .star_system_edges() - .find_many(vec![star_system_edges::universe_id::equals(universe_id.0)]) + .find_many(vec![star_system_edges::universe_id::equals(universe_id)]) .select(star_system_edges::select!({ from_system_id to_system_id })) .exec() .await diff --git a/backend/src/state/empire.rs b/backend/src/state/empire.rs index 918eddb..937f9fa 100644 --- a/backend/src/state/empire.rs +++ b/backend/src/state/empire.rs @@ -1,9 +1,15 @@ +use colourado::Color; + +use super::StarSystemId; + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct EmpireId(pub i32); pub struct EmpireCoreState { pub id: EmpireId, pub name: String, + pub color: Color, + pub capital_system_id: StarSystemId, } pub struct EmpireDerivedState {} diff --git a/backend/src/state/game.rs b/backend/src/state/game.rs index 8098858..943d3ec 100644 --- a/backend/src/state/game.rs +++ b/backend/src/state/game.rs @@ -1,10 +1,13 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use anyhow::Result; use chrono::{DateTime, Utc}; use petgraph::graphmap::UnGraphMap; -use crate::prisma::{empire, star_system, star_system_edges, PrismaClient}; +use crate::{ + prisma::{empire, star_system, star_system_edges, PrismaClient}, + utils::format_color, +}; use super::{EmpireCoreState, EmpireId, StarSystemCoreState, StarSystemId}; @@ -13,10 +16,12 @@ pub struct UniverseId(pub i32); pub struct GameCoreState { pub universe_id: UniverseId, + + /// The instant of time that this game state was captured at pub current_instant: DateTime, pub empires: HashMap, pub star_systems: HashMap, - pub hyperlanes: UnGraphMap, + pub hyperlanes: UnGraphMap, } pub struct GameDerivedState<'a> { @@ -26,6 +31,7 @@ pub struct GameDerivedState<'a> { impl GameCoreState { pub async fn save_to_database(&self, client: &PrismaClient) -> Result<()> { // Save star systems to database + let mut owned_systems = HashMap::new(); client .star_system() .create_many( @@ -33,6 +39,9 @@ impl GameCoreState { .star_systems .values() .map(|star_system| { + if let Some(empire_id) = star_system.owned_by_empire_id { + owned_systems.insert(star_system.id, empire_id); + } star_system::create_unchecked( star_system.id.0, self.universe_id.0, @@ -57,8 +66,8 @@ impl GameCoreState { .map(|edge| { star_system_edges::create_unchecked( self.universe_id.0, - edge.0 as i32, - edge.1 as i32, + edge.0 .0 as i32, + edge.1 .0 as i32, vec![], ) }) @@ -79,6 +88,7 @@ impl GameCoreState { empire::create_unchecked( self.universe_id.0, empire.name.clone(), + format_color(empire.color), vec![empire::id::set(empire.id.0)], ) }) @@ -88,6 +98,21 @@ impl GameCoreState { .exec() .await?; + // Update the ownership + for (star_system_id, empire_id) in owned_systems.into_iter() { + client + .star_system() + .update( + star_system::UniqueWhereParam::UniverseIdIndexEquals( + self.universe_id.0, + star_system_id.0, + ), + vec![star_system::owned_by_empire_id::set(Some(empire_id.0))], + ) + .exec() + .await?; + } + Ok(()) } } diff --git a/backend/src/state/starsystem.rs b/backend/src/state/starsystem.rs index 49e2c28..857c5dc 100644 --- a/backend/src/state/starsystem.rs +++ b/backend/src/state/starsystem.rs @@ -1,11 +1,14 @@ +use super::EmpireId; + /// This is only guaranteed to be unique for a specific UniverseId -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct StarSystemId(pub i32); #[derive(Debug)] pub struct StarSystemCoreState { pub id: StarSystemId, pub position: (f64, f64), + pub owned_by_empire_id: Option, } pub struct StarSystemDerivedState {} diff --git a/backend/src/utils.rs b/backend/src/utils.rs new file mode 100644 index 0000000..4ae1d06 --- /dev/null +++ b/backend/src/utils.rs @@ -0,0 +1,9 @@ +use colourado::Color; + +pub fn format_color(color: Color) -> String { + let red = (color.red * 255.0) as usize; + let green = (color.green * 255.0) as usize; + let blue = (color.blue * 255.0) as usize; + + format!("{:02x}{:02x}{:02x}", red, green, blue) +} diff --git a/frontend/src/components/EmpireSelector.tsx b/frontend/src/components/EmpireSelector.tsx index 26ceb6f..2be276f 100644 --- a/frontend/src/components/EmpireSelector.tsx +++ b/frontend/src/components/EmpireSelector.tsx @@ -1,6 +1,24 @@ -import { Button, InputGroup, Menu, Popover } from "@blueprintjs/core"; +import { Button, InputGroup, Menu, MenuItem, Popover } from "@blueprintjs/core"; +import { useQuery } from "react-query"; +import { useParams } from "react-router-dom"; +import { useAppDispatch, useAppSelector } from "../store"; +import { changeActiveEmpire } from "../store/someShit"; export default function EmpireSelector() { + const dispatch = useAppDispatch(); + const { universeId } = useParams(); + const { isLoading, data } = useQuery( + `universe ${universeId} empire list`, + () => + fetch(`/api/universe/${universeId}/empires`).then((res) => res.json()), + ); + const activeEmpireId = useAppSelector( + (state) => state.someShit.activeEmpireId, + ); + + if (isLoading) return <>...; + const empireMap = new Map(data.empires.map((empire) => [empire.id, empire])); + return ( - ... + {data.empires.map((empire) => ( + dispatch(changeActiveEmpire(empire.id))} + /> + ))} } placement="bottom-end" @@ -20,7 +45,11 @@ export default function EmpireSelector() { alignText="left" icon="user" rightIcon="caret-down" - text="(select empire)" + text={ + activeEmpireId + ? empireMap.get(activeEmpireId).name + : "(select empire)" + } /> ); diff --git a/frontend/src/routes/MapView.tsx b/frontend/src/routes/MapView.tsx index 95a9c80..549f4d1 100644 --- a/frontend/src/routes/MapView.tsx +++ b/frontend/src/routes/MapView.tsx @@ -4,20 +4,30 @@ import { Canvas, type MeshProps, useLoader } from "@react-three/fiber"; import styles from "./MapView.module.scss"; import { useParams } from "react-router-dom"; import { MapControls, Text } from "@react-three/drei"; -import { DoubleSide, TextureLoader } from "three"; +import { ColorRepresentation, DoubleSide, TextureLoader } from "three"; export default function MapView({}) { const { universeId } = useParams(); - const { isLoading, data } = useQuery(`universe ${universeId} points`, () => - fetch(`/api/universe/${universeId}/map`).then((res) => res.json()), + const { isLoading, data: mapData } = useQuery( + `universe ${universeId} points`, + () => fetch(`/api/universe/${universeId}/map`).then((res) => res.json()), + ); + const { isLoading: isEmpireLoading, data: empireData } = useQuery( + `universe ${universeId} empire list`, + () => + fetch(`/api/universe/${universeId}/empires`).then((res) => res.json()), ); const outerSpace = useLoader( TextureLoader, "https://s3-us-west-2.amazonaws.com/s.cdpn.io/96252/space.jpg", ); - if (isLoading) return <>...; - console.log("data", data); + if (isLoading || isEmpireLoading) return <>...; + console.log("data", empireData); + + const empireMap = new Map( + empireData.empires.map((empire) => [empire.id, empire]), + ); return (
@@ -30,33 +40,49 @@ export default function MapView({}) { - {data.points.map((point) => { + {mapData.points.map((point) => { const x = point.coordX / 80; const y = point.coordY / 80; return ( - + ); })} - {data.edges.map((edge) => { + {mapData.edges.map((edge) => { return ( ); })} + + {/* {empireData.empires.map((empire) => { + return ; + })} */}
); } -function StarSystem({ system, position, ...props }: MeshProps) { +function StarSystem({ system, position, empires, ...props }: MeshProps) { const meshRef = useRef(); const [hovered, setHover] = useState(false); const [active, setActive] = useState(false); + let color: ColorRepresentation = "skyblue"; + let owner; + if (system.ownedByEmpireId !== null) { + owner = empires.get(system.ownedByEmpireId); + color = parseInt(owner.color, 16); + } + return ( - - {system.index} - + {owner && ( + + {owner.name} + + )} - + ); } diff --git a/frontend/src/store/someShit.ts b/frontend/src/store/someShit.ts index 5de7581..4cbe025 100644 --- a/frontend/src/store/someShit.ts +++ b/frontend/src/store/someShit.ts @@ -1,10 +1,9 @@ import { createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; -import type { RootState } from "../../app/store"; // Define a type for the slice state interface SomeShitState { - activeEmpireId: string | null; + activeEmpireId: number | null; } // Define the initial state using that type @@ -13,27 +12,15 @@ const initialState: SomeShitState = { }; export const someShitSlice = createSlice({ - name: "counter", - // `createSlice` will infer the state type from the `initialState` argument + name: "someShit", initialState, reducers: { - increment: (state) => { - state.value += 1; - }, - decrement: (state) => { - state.value -= 1; - }, - // Use the PayloadAction type to declare the contents of `action.payload` - incrementByAmount: (state, action: PayloadAction) => { - state.value += action.payload; + changeActiveEmpire: (state, action: PayloadAction) => { + state.activeEmpireId = action.payload; }, }, }); -export const { increment, decrement, incrementByAmount } = - someShitSlice.actions; - -// Other code such as selectors can use the imported `RootState` type -export const selectCount = (state: RootState) => state.counter.value; +export const { changeActiveEmpire } = someShitSlice.actions; export default someShitSlice.reducer; diff --git a/prisma-cli/Cargo.toml b/prisma-cli/Cargo.toml index e60e67a..d565f6e 100644 --- a/prisma-cli/Cargo.toml +++ b/prisma-cli/Cargo.toml @@ -4,5 +4,13 @@ version = "0.1.0" edition = "2021" [dependencies] -prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust", tag = "0.6.10" } -prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust", tag = "0.6.10" } + +[dependencies.prisma-client-rust] +git = "https://github.com/Brendonovich/prisma-client-rust" +tag = "0.6.10" +features = ["rspc"] + +[dependencies.prisma-client-rust-cli] +git = "https://github.com/Brendonovich/prisma-client-rust" +tag = "0.6.10" +features = ["rspc"] diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 65afa2d..99c301f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -42,16 +42,23 @@ model Player { } model Empire { - id Int @id @default(autoincrement()) + id Int @default(autoincrement()) universeId Int universe Universe @relation(fields: [universeId], references: [id]) name String - color String? + color String + + // capitalSystemId Int + // capitalSystem StarSystem @relation("capital", fields: [universeId, capitalSystemId], references: [universeId, index]) playerId String? @unique player Player? @relation(fields: [playerId], references: [id]) + + starSystems StarSystem[] @relation("owner") + + @@id([universeId, id]) } model StarSystem { @@ -65,6 +72,9 @@ model StarSystem { inbound StarSystemEdges[] @relation("inbound") outbound StarSystemEdges[] @relation("outbound") + ownedByEmpireId Int? + ownedByEmpire Empire? @relation("owner", fields: [universeId, ownedByEmpireId], references: [universeId, id]) + @@id([universeId, index]) }