refactor to make generation easier

This commit is contained in:
Michael Zhang 2023-12-30 04:07:31 -05:00
parent 88bb954676
commit 7bb16a1cca
19 changed files with 414 additions and 150 deletions

21
Cargo.lock generated
View file

@ -252,6 +252,7 @@ dependencies = [
"axum", "axum",
"chrono", "chrono",
"clap", "clap",
"petgraph 0.6.4",
"prisma-client-rust", "prisma-client-rust",
"serde", "serde",
"serde_json", "serde_json",
@ -1250,6 +1251,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.28" version = "1.0.28"
@ -3002,10 +3009,20 @@ version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset 0.1.9",
"ordermap", "ordermap",
] ]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset 0.4.2",
"indexmap 2.1.0",
]
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.11.2" version = "0.11.2"
@ -3447,7 +3464,7 @@ dependencies = [
"once_cell", "once_cell",
"opentelemetry", "opentelemetry",
"parking_lot 0.12.1", "parking_lot 0.12.1",
"petgraph", "petgraph 0.4.13",
"pin-utils", "pin-utils",
"prisma-models", "prisma-models",
"prisma-value", "prisma-value",

View file

@ -14,3 +14,4 @@ prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust
serde = { version = "1.0.193", features = ["derive"] } serde = { version = "1.0.193", features = ["derive"] }
tokio = { version = "1.35.1", features = ["full"] } tokio = { version = "1.35.1", features = ["full"] }
serde_json = "1.0.108" serde_json = "1.0.108"
petgraph = "0.6.4"

67
backend/src/generate.rs Normal file
View file

@ -0,0 +1,67 @@
use std::{collections::HashMap, f64::consts::PI};
use anyhow::Result;
use chrono::Utc;
use petgraph::{graph::UnGraph, graphmap::UnGraphMap};
use triangle::{triangulate, Point, TrianglulateOpts};
use crate::{
prisma::PrismaClient,
state::{GameCoreState, StarSystemCoreState, StarSystemId, UniverseId},
};
pub async fn generate_initial_game_state(
client: &PrismaClient,
) -> Result<GameCoreState> {
// Generate a new triangulation
let result = {
// Generate a circle of points
let radius = 1_000.0;
const NUM_POINTS: usize = 30;
let point_list = (0..NUM_POINTS)
.map(|n| n as f64 * (2.0 * PI) / NUM_POINTS as f64)
.map(|theta| Point {
x: radius * theta.cos(),
y: radius * theta.sin(),
});
triangulate(
TrianglulateOpts::builder()
.point_list(point_list)
.voronoi(true)
.maximum_triangle_area(6_000.0)
.build()?,
)?
};
// Generate a new universe ID
let universe = client.universe().create(json!({}), vec![]).exec().await?;
let empires = HashMap::new();
// Format the star systems
let mut star_systems = HashMap::new();
for (idx, point) in result.point_list.into_iter().enumerate() {
let id = StarSystemId(idx as i32);
let star_system = StarSystemCoreState {
id,
position: (point.x, point.y),
};
star_systems.insert(id, star_system);
}
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], ());
}
Ok(GameCoreState {
universe_id: UniverseId(universe.id),
current_instant: Utc::now(),
empires,
star_systems,
hyperlanes,
})
}

View file

@ -1,22 +1,22 @@
#[macro_use] #[macro_use]
extern crate serde_json; extern crate serde_json;
mod generate;
#[allow(unused_imports, dead_code)] #[allow(unused_imports, dead_code)]
pub mod prisma; pub mod prisma;
mod routes; mod routes;
pub mod state; pub mod state;
use std::{collections::HashMap, f64::consts::PI, sync::Arc}; use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use prisma::PrismaClient; use prisma::PrismaClient;
use prisma_client_rust::serde_json::json;
use routes::{universe_list, universe_map};
use triangle::{triangulate, Point, TrianglulateOpts};
use crate::prisma::{star_system, star_system_edges}; use routes::{universe_list, universe_map};
use crate::generate::generate_initial_game_state;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Opt { struct Opt {
@ -57,101 +57,8 @@ async fn main() -> Result<()> {
} }
Command::Seed => { Command::Seed => {
// Generate a new triangulation let game_state = generate_initial_game_state(&client).await?;
let result = { game_state.save_to_database(&client).await?;
// Generate a circle of points
let radius = 1000.0;
const NUM_POINTS: usize = 30;
let point_list = (0..NUM_POINTS)
.map(|n| n as f64 * (2.0 * PI) / NUM_POINTS as f64)
.map(|theta| Point {
x: radius * theta.cos(),
y: radius * theta.sin(),
});
triangulate(
TrianglulateOpts::builder()
.point_list(point_list)
.voronoi(true)
.maximum_triangle_area(4000.0)
.build()?,
)?
};
println!("Result: {result:?}");
// Insert into database
let universe = client.universe().create(json!({}), vec![]).exec().await?;
client
.star_system()
.create_many(
result
.point_list
.into_iter()
.enumerate()
.map(|(idx, point)| {
star_system::create_unchecked(
universe.id.clone(),
idx as i32,
point.x,
point.y,
vec![],
)
})
.collect(),
)
.exec()
.await?;
// Get the idx -> star system id mapping
let star_system_map = client
.star_system()
.find_many(vec![])
.select(star_system::select!({ id gen_index }))
.exec()
.await?
.into_iter()
.map(|star_system| (star_system.gen_index as usize, star_system.id))
.collect::<HashMap<_, _>>();
// Insert edges into database
macro_rules! create_edge {
($from:expr, $to:expr) => {{
println!("WTF? {}, {}", $from, $to);
star_system_edges::create_unchecked(
universe.id.clone(),
star_system_map.get(&$from).unwrap().to_owned(),
$from as i32,
star_system_map.get(&$to).unwrap().to_owned(),
$to as i32,
vec![],
)
}};
}
client
.star_system_edges()
.create_many(
result
.triangle_list
.into_iter()
.flat_map(|points| {
let a = points[0];
let b = points[1];
let c = points[2];
[
create_edge!(a, b),
create_edge!(a, c),
create_edge!(b, a),
create_edge!(b, c),
create_edge!(c, a),
create_edge!(c, b),
]
})
.collect(),
)
.skip_duplicates()
.exec()
.await?;
println!("Done."); println!("Done.");
} }

View file

@ -7,6 +7,7 @@ use serde_json::Value;
use crate::{ use crate::{
prisma::{star_system, star_system_edges}, prisma::{star_system, star_system_edges},
state::UniverseId,
AppState, AppState,
}; };
@ -25,17 +26,17 @@ pub async fn universe_list(state: State<AppState>) -> Json<Value> {
} }
pub async fn universe_map( pub async fn universe_map(
Path((universe_id,)): Path<(String,)>, Path((universe_id,)): Path<(i32,)>,
state: State<AppState>, state: State<AppState>,
) -> Json<Value> { ) -> Json<Value> {
println!("universe id {universe_id}"); let universe_id = UniverseId(universe_id);
let points = state let points = state
.prisma .prisma
.star_system() .star_system()
.find_many(vec![star_system::universe_id::equals(universe_id.clone())]) .find_many(vec![star_system::universe_id::equals(universe_id.0)])
.order_by(star_system::gen_index::order(Direction::Asc)) .order_by(star_system::index::order(Direction::Asc))
.select(star_system::select!({ id coord_x coord_y })) .select(star_system::select!({ index coord_x coord_y }))
.exec() .exec()
.await .await
.unwrap(); .unwrap();
@ -43,10 +44,8 @@ pub async fn universe_map(
let edges = state let edges = state
.prisma .prisma
.star_system_edges() .star_system_edges()
.find_many(vec![star_system_edges::universe_id::equals( .find_many(vec![star_system_edges::universe_id::equals(universe_id.0)])
universe_id.clone(), .select(star_system_edges::select!({ from_system_id to_system_id }))
)])
.select(star_system_edges::select!({ from_system_id to_system_id from_index to_index }))
.exec() .exec()
.await .await
.unwrap(); .unwrap();

View file

@ -1,15 +1,87 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::Result;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use petgraph::{graph::UnGraph, graphmap::UnGraphMap};
use super::{EmpireCoreState, EmpireId}; use crate::prisma::{star_system, star_system_edges, PrismaClient};
use super::{EmpireCoreState, EmpireId, StarSystemCoreState, StarSystemId};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct UniverseId(pub i32);
pub struct GameCoreState { pub struct GameCoreState {
instant: DateTime<Utc>, pub universe_id: UniverseId,
decision_time: DateTime<Utc>, pub current_instant: DateTime<Utc>,
empires: HashMap<EmpireId, EmpireCoreState>, pub empires: HashMap<EmpireId, EmpireCoreState>,
pub star_systems: HashMap<StarSystemId, StarSystemCoreState>,
pub hyperlanes: UnGraphMap<usize, ()>,
} }
pub struct GameDerivedState<'a> { pub struct GameDerivedState<'a> {
core: &'a GameCoreState, core: &'a GameCoreState,
} }
impl GameCoreState {
pub async fn save_to_database(&self, client: &PrismaClient) -> Result<()> {
println!("Self {:?} {:?}", self.universe_id, self.star_systems);
// Save star systems to database
client
.star_system()
.create_many(
self
.star_systems
.values()
.map(|star_system| {
star_system::create_unchecked(
star_system.id.0,
self.universe_id.0,
star_system.position.0,
star_system.position.1,
vec![],
)
})
.collect(),
)
.skip_duplicates()
.exec()
.await?;
// Insert edges into database
macro_rules! create_edge {
($from:expr, $to:expr) => {{
println!("WTF? {}, {}", $from, $to);
star_system_edges::create_unchecked(
universe.id.clone(),
star_system_map.get(&$from).unwrap().to_owned(),
$from as i32,
star_system_map.get(&$to).unwrap().to_owned(),
$to as i32,
vec![],
)
}};
}
client
.star_system_edges()
.create_many(
self
.hyperlanes
.all_edges()
.map(|edge| {
star_system_edges::create_unchecked(
self.universe_id.0,
edge.0 as i32,
edge.1 as i32,
vec![],
)
})
.collect(),
)
.skip_duplicates()
.exec()
.await?;
Ok(())
}
}

View file

@ -0,0 +1 @@

View file

@ -1,3 +1,6 @@
#[macro_use]
mod macros;
mod empire; mod empire;
mod fleet; mod fleet;
mod game; mod game;

View file

@ -1,3 +1,11 @@
pub struct StarSystemCoreState {} /// This is only guaranteed to be unique for a specific UniverseId
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct StarSystemId(pub i32);
#[derive(Debug)]
pub struct StarSystemCoreState {
pub id: StarSystemId,
pub position: (f64, f64),
}
pub struct StarSystemDerivedState {} pub struct StarSystemDerivedState {}

View file

@ -18,11 +18,13 @@
"@blueprintjs/table": "^5.0.20", "@blueprintjs/table": "^5.0.20",
"@react-three/drei": "^9.92.7", "@react-three/drei": "^9.92.7",
"@react-three/fiber": "^8.15.12", "@react-three/fiber": "^8.15.12",
"@reduxjs/toolkit": "^2.0.1",
"@types/three": "^0.160.0", "@types/three": "^0.160.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-redux": "^9.0.4",
"react-router-dom": "^6.21.1", "react-router-dom": "^6.21.1",
"three": "^0.160.0" "three": "^0.160.0"
}, },

View file

@ -20,6 +20,9 @@ dependencies:
'@react-three/fiber': '@react-three/fiber':
specifier: ^8.15.12 specifier: ^8.15.12
version: 8.15.12(react-dom@18.2.0)(react@18.2.0)(three@0.160.0) version: 8.15.12(react-dom@18.2.0)(react@18.2.0)(three@0.160.0)
'@reduxjs/toolkit':
specifier: ^2.0.1
version: 2.0.1(react-redux@9.0.4)(react@18.2.0)
'@types/three': '@types/three':
specifier: ^0.160.0 specifier: ^0.160.0
version: 0.160.0 version: 0.160.0
@ -35,6 +38,9 @@ dependencies:
react-query: react-query:
specifier: ^3.39.3 specifier: ^3.39.3
version: 3.39.3(react-dom@18.2.0)(react@18.2.0) version: 3.39.3(react-dom@18.2.0)(react@18.2.0)
react-redux:
specifier: ^9.0.4
version: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
react-router-dom: react-router-dom:
specifier: ^6.21.1 specifier: ^6.21.1
version: 6.21.1(react-dom@18.2.0)(react@18.2.0) version: 6.21.1(react-dom@18.2.0)(react@18.2.0)
@ -591,6 +597,25 @@ packages:
zustand: 3.7.2(react@18.2.0) zustand: 3.7.2(react@18.2.0)
dev: false dev: false
/@reduxjs/toolkit@2.0.1(react-redux@9.0.4)(react@18.2.0):
resolution: {integrity: sha512-fxIjrR9934cmS8YXIGd9e7s1XRsEU++aFc9DVNMFMRTM5Vtsg2DCRMj21eslGtDt43IUf9bJL3h5bwUlZleibA==}
peerDependencies:
react: ^16.9.0 || ^17.0.0 || ^18
react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0
peerDependenciesMeta:
react:
optional: true
react-redux:
optional: true
dependencies:
immer: 10.0.3
react: 18.2.0
react-redux: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
redux: 5.0.1
redux-thunk: 3.1.0(redux@5.0.1)
reselect: 5.0.1
dev: false
/@remix-run/router@1.14.1: /@remix-run/router@1.14.1:
resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==} resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -883,6 +908,10 @@ packages:
meshoptimizer: 0.18.1 meshoptimizer: 0.18.1
dev: false dev: false
/@types/use-sync-external-store@0.0.3:
resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
dev: false
/@types/webxr@0.5.10: /@types/webxr@0.5.10:
resolution: {integrity: sha512-n3u5sqXQJhf1CS68mw3Wf16FQ4cRPNBBwdYLFzq3UddiADOim1Pn3Y6PBdDilz1vOJF3ybLxJ8ZEDlLIzrOQZg==} resolution: {integrity: sha512-n3u5sqXQJhf1CS68mw3Wf16FQ4cRPNBBwdYLFzq3UddiADOim1Pn3Y6PBdDilz1vOJF3ybLxJ8ZEDlLIzrOQZg==}
dev: false dev: false
@ -1633,6 +1662,10 @@ packages:
engines: {node: '>= 4'} engines: {node: '>= 4'}
dev: true dev: true
/immer@10.0.3:
resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==}
dev: false
/immutable@4.3.4: /immutable@4.3.4:
resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==}
dev: true dev: true
@ -2093,6 +2126,28 @@ packages:
scheduler: 0.21.0 scheduler: 0.21.0
dev: false dev: false
/react-redux@9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1):
resolution: {integrity: sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA==}
peerDependencies:
'@types/react': ^18.2.25
react: ^18.0
react-native: '>=0.69'
redux: ^5.0.0
peerDependenciesMeta:
'@types/react':
optional: true
react-native:
optional: true
redux:
optional: true
dependencies:
'@types/react': 18.2.46
'@types/use-sync-external-store': 0.0.3
react: 18.2.0
redux: 5.0.1
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
/react-router-dom@6.21.1(react-dom@18.2.0)(react@18.2.0): /react-router-dom@6.21.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==} resolution: {integrity: sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -2155,6 +2210,18 @@ packages:
picomatch: 2.3.1 picomatch: 2.3.1
dev: true dev: true
/redux-thunk@3.1.0(redux@5.0.1):
resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==}
peerDependencies:
redux: ^5.0.0
dependencies:
redux: 5.0.1
dev: false
/redux@5.0.1:
resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==}
dev: false
/regenerator-runtime@0.14.1: /regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
dev: false dev: false
@ -2168,6 +2235,10 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: false dev: false
/reselect@5.0.1:
resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==}
dev: false
/resolve-from@4.0.0: /resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -2428,6 +2499,14 @@ packages:
punycode: 2.3.1 punycode: 2.3.1
dev: true dev: true
/use-sync-external-store@1.2.0(react@18.2.0):
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
dev: false
/utility-types@3.10.0: /utility-types@3.10.0:
resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==} resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==}
engines: {node: '>= 4'} engines: {node: '>= 4'}

View file

@ -2,15 +2,19 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "react-query"; import { QueryClient, QueryClientProvider } from "react-query";
import "./App.css"; import "./App.css";
import routes from "./routes"; import routes from "./routes";
import { Provider as ReduxProvider } from "react-redux";
import { store } from "./store";
const router = createBrowserRouter(routes); const router = createBrowserRouter(routes);
const queryClient = new QueryClient(); const queryClient = new QueryClient();
function App() { function App() {
return ( return (
<QueryClientProvider client={queryClient}> <ReduxProvider store={store}>
<RouterProvider router={router} /> <QueryClientProvider client={queryClient}>
</QueryClientProvider> <RouterProvider router={router} />
</QueryClientProvider>
</ReduxProvider>
); );
} }

View file

@ -1,8 +1,22 @@
import { Outlet, Link, useParams } from "react-router-dom"; import { Outlet, Link, useParams } from "react-router-dom";
import { Alignment, Icon, Navbar } from "@blueprintjs/core"; import {
Alignment,
Button,
Icon,
InputGroup,
Menu,
Navbar,
Popover,
} from "@blueprintjs/core";
import { useAppSelector } from "./store";
import EmpireSelector from "./components/EmpireSelector";
export default function Layout() { export default function Layout() {
const { universeId } = useParams(); const { universeId } = useParams();
const activeEmpireId = useAppSelector(
(state) => state.someShit.activeEmpireId,
);
return ( return (
<> <>
<Navbar> <Navbar>
@ -33,6 +47,10 @@ export default function Layout() {
</> </>
)} )}
</Navbar.Group> </Navbar.Group>
<Navbar.Group align={Alignment.RIGHT}>
<EmpireSelector />
</Navbar.Group>
</Navbar> </Navbar>
<Outlet /> <Outlet />

View file

@ -0,0 +1,27 @@
import { Button, InputGroup, Menu, Popover } from "@blueprintjs/core";
export default function EmpireSelector() {
return (
<Popover
content={
<Menu>
<InputGroup
asyncControl={true}
leftIcon="search"
placeholder="Search for empire..."
autoFocus
/>
...
</Menu>
}
placement="bottom-end"
>
<Button
alignText="left"
icon="user"
rightIcon="caret-down"
text="(select empire)"
/>
</Popover>
);
}

View file

@ -1,14 +1,9 @@
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import { import { Canvas, type MeshProps, useLoader } from "@react-three/fiber";
Canvas,
useFrame,
type MeshProps,
useLoader,
} from "@react-three/fiber";
import styles from "./MapView.module.scss"; import styles from "./MapView.module.scss";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { MapControls } from "@react-three/drei"; import { MapControls, Text } from "@react-three/drei";
import { DoubleSide, TextureLoader } from "three"; import { DoubleSide, TextureLoader } from "three";
export default function MapView({}) { export default function MapView({}) {
@ -38,13 +33,15 @@ export default function MapView({}) {
{data.points.map((point) => { {data.points.map((point) => {
const x = point.coordX / 80; const x = point.coordX / 80;
const y = point.coordY / 80; const y = point.coordY / 80;
return <StarSystem key={point.id} position={[x, 0, y]} />; return (
<StarSystem key={point.id} system={point} position={[x, 0, y]} />
);
})} })}
{data.edges.map((edge) => { {data.edges.map((edge) => {
return ( return (
<Hyperlane <Hyperlane
key={`${edge.fromIndex}:${edge.toIndex}`} key={`${edge.fromSystemId}:${edge.toSystemId}`}
edge={edge} edge={edge}
points={data.points} points={data.points}
/> />
@ -55,7 +52,7 @@ export default function MapView({}) {
); );
} }
function StarSystem({ position, ...props }: MeshProps) { function StarSystem({ system, position, ...props }: MeshProps) {
const meshRef = useRef(); const meshRef = useRef();
const [hovered, setHover] = useState(false); const [hovered, setHover] = useState(false);
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
@ -71,14 +68,25 @@ function StarSystem({ position, ...props }: MeshProps) {
onPointerOut={(_event) => setHover(false)} onPointerOut={(_event) => setHover(false)}
> >
<sphereGeometry args={[0.05]} /> <sphereGeometry args={[0.05]} />
<Text
color="white"
fontSize={0.18}
anchorX="center"
anchorY="middle"
position={[0, 0.1, 0]}
>
{system.index}
</Text>
<meshStandardMaterial color={hovered ? "white" : "skyblue"} /> <meshStandardMaterial color={hovered ? "white" : "skyblue"} />
</mesh> </mesh>
); );
} }
function Hyperlane({ edge, points, ...props }) { function Hyperlane({ edge, points, ...props }) {
const fromPoint = points[edge.fromIndex]; const fromPoint = points[edge.fromSystemId];
const toPoint = points[edge.toIndex]; const toPoint = points[edge.toSystemId];
const avgX = (fromPoint.coordX + toPoint.coordX) / 160; const avgX = (fromPoint.coordX + toPoint.coordX) / 160;
const avgY = (fromPoint.coordY + toPoint.coordY) / 160; const avgY = (fromPoint.coordY + toPoint.coordY) / 160;

View file

@ -0,0 +1,13 @@
import { configureStore } from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import someShit from "./someShit";
export const store = configureStore({
reducer: { someShit },
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

View file

@ -0,0 +1,39 @@
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;
}
// Define the initial state using that type
const initialState: SomeShitState = {
activeEmpireId: null,
};
export const someShitSlice = createSlice({
name: "counter",
// `createSlice` will infer the state type from the `initialState` argument
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<number>) => {
state.value += 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 default someShitSlice.reducer;

View file

@ -1,3 +1,3 @@
fn main() { fn main() {
prisma_client_rust_cli::run(); prisma_client_rust_cli::run();
} }

View file

@ -9,7 +9,7 @@ datasource db {
} }
model Universe { model Universe {
id String @id @default(uuid()) id Int @id @default(autoincrement())
config Json config Json
@ -35,7 +35,7 @@ model User {
model Player { model Player {
id String @id @default(uuid()) id String @id @default(uuid())
universeId String universeId Int
universe Universe @relation(fields: [universeId], references: [id]) universe Universe @relation(fields: [universeId], references: [id])
empire Empire? empire Empire?
@ -44,7 +44,7 @@ model Player {
model Empire { model Empire {
id String @id @default(uuid()) id String @id @default(uuid())
universeId String universeId Int
universe Universe @relation(fields: [universeId], references: [id]) universe Universe @relation(fields: [universeId], references: [id])
name String name String
@ -55,29 +55,28 @@ model Empire {
} }
model StarSystem { model StarSystem {
id String @id @default(uuid()) index Int
universeId String universeId Int
universe Universe @relation(fields: [universeId], references: [id]) universe Universe @relation(fields: [universeId], references: [id])
genIndex Int coordX Float
coordX Float coordY Float
coordY Float
inbound StarSystemEdges[] @relation("inbound") inbound StarSystemEdges[] @relation("inbound")
outbound StarSystemEdges[] @relation("outbound") outbound StarSystemEdges[] @relation("outbound")
@@id([universeId, index])
} }
model StarSystemEdges { model StarSystemEdges {
universeId String universeId Int
universe Universe @relation(fields: [universeId], references: [id]) universe Universe @relation(fields: [universeId], references: [id])
fromSystemId String fromSystemId Int
from StarSystem @relation(fields: [fromSystemId], references: [id], name: "outbound") from StarSystem @relation(fields: [universeId, fromSystemId], references: [universeId, index], name: "outbound")
fromIndex Int
toSystemId String toSystemId Int
to StarSystem @relation(fields: [toSystemId], references: [id], name: "inbound") to StarSystem @relation(fields: [universeId, toSystemId], references: [universeId, index], name: "inbound")
toIndex Int
@@id([universeId, fromSystemId, toSystemId]) @@id([universeId, fromSystemId, toSystemId])
} }
@ -85,14 +84,14 @@ model StarSystemEdges {
model Planet { model Planet {
id String @id @default(uuid()) id String @id @default(uuid())
universeId String universeId Int
universe Universe @relation(fields: [universeId], references: [id]) universe Universe @relation(fields: [universeId], references: [id])
} }
model Fleet { model Fleet {
id String @id @default(uuid()) id String @id @default(uuid())
universeId String universeId Int
universe Universe @relation(fields: [universeId], references: [id]) universe Universe @relation(fields: [universeId], references: [id])
} }