refactor to make generation easier
This commit is contained in:
parent
88bb954676
commit
7bb16a1cca
19 changed files with 414 additions and 150 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -252,6 +252,7 @@ dependencies = [
|
|||
"axum",
|
||||
"chrono",
|
||||
"clap",
|
||||
"petgraph 0.6.4",
|
||||
"prisma-client-rust",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1250,6 +1251,12 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
|
@ -3002,10 +3009,20 @@ version = "0.4.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"fixedbitset 0.1.9",
|
||||
"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]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
|
@ -3447,7 +3464,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"opentelemetry",
|
||||
"parking_lot 0.12.1",
|
||||
"petgraph",
|
||||
"petgraph 0.4.13",
|
||||
"pin-utils",
|
||||
"prisma-models",
|
||||
"prisma-value",
|
||||
|
|
|
@ -14,3 +14,4 @@ prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust
|
|||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
tokio = { version = "1.35.1", features = ["full"] }
|
||||
serde_json = "1.0.108"
|
||||
petgraph = "0.6.4"
|
||||
|
|
67
backend/src/generate.rs
Normal file
67
backend/src/generate.rs
Normal 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,
|
||||
})
|
||||
}
|
|
@ -1,22 +1,22 @@
|
|||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
mod generate;
|
||||
#[allow(unused_imports, dead_code)]
|
||||
pub mod prisma;
|
||||
mod routes;
|
||||
pub mod state;
|
||||
|
||||
use std::{collections::HashMap, f64::consts::PI, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use axum::{routing::get, Router};
|
||||
use clap::{Parser, Subcommand};
|
||||
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)]
|
||||
struct Opt {
|
||||
|
@ -57,101 +57,8 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
Command::Seed => {
|
||||
// Generate a new triangulation
|
||||
let result = {
|
||||
// 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?;
|
||||
let game_state = generate_initial_game_state(&client).await?;
|
||||
game_state.save_to_database(&client).await?;
|
||||
|
||||
println!("Done.");
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use serde_json::Value;
|
|||
|
||||
use crate::{
|
||||
prisma::{star_system, star_system_edges},
|
||||
state::UniverseId,
|
||||
AppState,
|
||||
};
|
||||
|
||||
|
@ -25,17 +26,17 @@ pub async fn universe_list(state: State<AppState>) -> Json<Value> {
|
|||
}
|
||||
|
||||
pub async fn universe_map(
|
||||
Path((universe_id,)): Path<(String,)>,
|
||||
Path((universe_id,)): Path<(i32,)>,
|
||||
state: State<AppState>,
|
||||
) -> Json<Value> {
|
||||
println!("universe id {universe_id}");
|
||||
let universe_id = UniverseId(universe_id);
|
||||
|
||||
let points = state
|
||||
.prisma
|
||||
.star_system()
|
||||
.find_many(vec![star_system::universe_id::equals(universe_id.clone())])
|
||||
.order_by(star_system::gen_index::order(Direction::Asc))
|
||||
.select(star_system::select!({ id coord_x coord_y }))
|
||||
.find_many(vec![star_system::universe_id::equals(universe_id.0)])
|
||||
.order_by(star_system::index::order(Direction::Asc))
|
||||
.select(star_system::select!({ index coord_x coord_y }))
|
||||
.exec()
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -43,10 +44,8 @@ pub async fn universe_map(
|
|||
let edges = state
|
||||
.prisma
|
||||
.star_system_edges()
|
||||
.find_many(vec![star_system_edges::universe_id::equals(
|
||||
universe_id.clone(),
|
||||
)])
|
||||
.select(star_system_edges::select!({ from_system_id to_system_id from_index to_index }))
|
||||
.find_many(vec![star_system_edges::universe_id::equals(universe_id.0)])
|
||||
.select(star_system_edges::select!({ from_system_id to_system_id }))
|
||||
.exec()
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
@ -1,15 +1,87 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
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 {
|
||||
instant: DateTime<Utc>,
|
||||
decision_time: DateTime<Utc>,
|
||||
empires: HashMap<EmpireId, EmpireCoreState>,
|
||||
pub universe_id: UniverseId,
|
||||
pub current_instant: DateTime<Utc>,
|
||||
pub empires: HashMap<EmpireId, EmpireCoreState>,
|
||||
pub star_systems: HashMap<StarSystemId, StarSystemCoreState>,
|
||||
pub hyperlanes: UnGraphMap<usize, ()>,
|
||||
}
|
||||
|
||||
pub struct GameDerivedState<'a> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
1
backend/src/state/macros.rs
Normal file
1
backend/src/state/macros.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod empire;
|
||||
mod fleet;
|
||||
mod game;
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
"@blueprintjs/table": "^5.0.20",
|
||||
"@react-three/drei": "^9.92.7",
|
||||
"@react-three/fiber": "^8.15.12",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@types/three": "^0.160.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-query": "^3.39.3",
|
||||
"react-redux": "^9.0.4",
|
||||
"react-router-dom": "^6.21.1",
|
||||
"three": "^0.160.0"
|
||||
},
|
||||
|
|
|
@ -20,6 +20,9 @@ dependencies:
|
|||
'@react-three/fiber':
|
||||
specifier: ^8.15.12
|
||||
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':
|
||||
specifier: ^0.160.0
|
||||
version: 0.160.0
|
||||
|
@ -35,6 +38,9 @@ dependencies:
|
|||
react-query:
|
||||
specifier: ^3.39.3
|
||||
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:
|
||||
specifier: ^6.21.1
|
||||
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)
|
||||
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:
|
||||
resolution: {integrity: sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
@ -883,6 +908,10 @@ packages:
|
|||
meshoptimizer: 0.18.1
|
||||
dev: false
|
||||
|
||||
/@types/use-sync-external-store@0.0.3:
|
||||
resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
|
||||
dev: false
|
||||
|
||||
/@types/webxr@0.5.10:
|
||||
resolution: {integrity: sha512-n3u5sqXQJhf1CS68mw3Wf16FQ4cRPNBBwdYLFzq3UddiADOim1Pn3Y6PBdDilz1vOJF3ybLxJ8ZEDlLIzrOQZg==}
|
||||
dev: false
|
||||
|
@ -1633,6 +1662,10 @@ packages:
|
|||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/immer@10.0.3:
|
||||
resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==}
|
||||
dev: false
|
||||
|
||||
/immutable@4.3.4:
|
||||
resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==}
|
||||
dev: true
|
||||
|
@ -2093,6 +2126,28 @@ packages:
|
|||
scheduler: 0.21.0
|
||||
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):
|
||||
resolution: {integrity: sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
@ -2155,6 +2210,18 @@ packages:
|
|||
picomatch: 2.3.1
|
||||
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:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
dev: false
|
||||
|
@ -2168,6 +2235,10 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/reselect@5.0.1:
|
||||
resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==}
|
||||
dev: false
|
||||
|
||||
/resolve-from@4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -2428,6 +2499,14 @@ packages:
|
|||
punycode: 2.3.1
|
||||
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:
|
||||
resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
|
|
@ -2,15 +2,19 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
|||
import { QueryClient, QueryClientProvider } from "react-query";
|
||||
import "./App.css";
|
||||
import routes from "./routes";
|
||||
import { Provider as ReduxProvider } from "react-redux";
|
||||
import { store } from "./store";
|
||||
|
||||
const router = createBrowserRouter(routes);
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ReduxProvider store={store}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
</QueryClientProvider>
|
||||
</ReduxProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,22 @@
|
|||
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() {
|
||||
const { universeId } = useParams();
|
||||
const activeEmpireId = useAppSelector(
|
||||
(state) => state.someShit.activeEmpireId,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar>
|
||||
|
@ -33,6 +47,10 @@ export default function Layout() {
|
|||
</>
|
||||
)}
|
||||
</Navbar.Group>
|
||||
|
||||
<Navbar.Group align={Alignment.RIGHT}>
|
||||
<EmpireSelector />
|
||||
</Navbar.Group>
|
||||
</Navbar>
|
||||
|
||||
<Outlet />
|
||||
|
|
27
frontend/src/components/EmpireSelector.tsx
Normal file
27
frontend/src/components/EmpireSelector.tsx
Normal 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>
|
||||
);
|
||||
}
|
|
@ -1,14 +1,9 @@
|
|||
import { useRef, useState } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import {
|
||||
Canvas,
|
||||
useFrame,
|
||||
type MeshProps,
|
||||
useLoader,
|
||||
} from "@react-three/fiber";
|
||||
import { Canvas, type MeshProps, useLoader } from "@react-three/fiber";
|
||||
import styles from "./MapView.module.scss";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { MapControls } from "@react-three/drei";
|
||||
import { MapControls, Text } from "@react-three/drei";
|
||||
import { DoubleSide, TextureLoader } from "three";
|
||||
|
||||
export default function MapView({}) {
|
||||
|
@ -38,13 +33,15 @@ export default function MapView({}) {
|
|||
{data.points.map((point) => {
|
||||
const x = point.coordX / 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) => {
|
||||
return (
|
||||
<Hyperlane
|
||||
key={`${edge.fromIndex}:${edge.toIndex}`}
|
||||
key={`${edge.fromSystemId}:${edge.toSystemId}`}
|
||||
edge={edge}
|
||||
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 [hovered, setHover] = useState(false);
|
||||
const [active, setActive] = useState(false);
|
||||
|
@ -71,14 +68,25 @@ function StarSystem({ position, ...props }: MeshProps) {
|
|||
onPointerOut={(_event) => setHover(false)}
|
||||
>
|
||||
<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"} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
function Hyperlane({ edge, points, ...props }) {
|
||||
const fromPoint = points[edge.fromIndex];
|
||||
const toPoint = points[edge.toIndex];
|
||||
const fromPoint = points[edge.fromSystemId];
|
||||
const toPoint = points[edge.toSystemId];
|
||||
|
||||
const avgX = (fromPoint.coordX + toPoint.coordX) / 160;
|
||||
const avgY = (fromPoint.coordY + toPoint.coordY) / 160;
|
||||
|
|
13
frontend/src/store/index.ts
Normal file
13
frontend/src/store/index.ts
Normal 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;
|
39
frontend/src/store/someShit.ts
Normal file
39
frontend/src/store/someShit.ts
Normal 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;
|
|
@ -9,7 +9,7 @@ datasource db {
|
|||
}
|
||||
|
||||
model Universe {
|
||||
id String @id @default(uuid())
|
||||
id Int @id @default(autoincrement())
|
||||
|
||||
config Json
|
||||
|
||||
|
@ -35,7 +35,7 @@ model User {
|
|||
model Player {
|
||||
id String @id @default(uuid())
|
||||
|
||||
universeId String
|
||||
universeId Int
|
||||
universe Universe @relation(fields: [universeId], references: [id])
|
||||
|
||||
empire Empire?
|
||||
|
@ -44,7 +44,7 @@ model Player {
|
|||
model Empire {
|
||||
id String @id @default(uuid())
|
||||
|
||||
universeId String
|
||||
universeId Int
|
||||
universe Universe @relation(fields: [universeId], references: [id])
|
||||
|
||||
name String
|
||||
|
@ -55,29 +55,28 @@ model Empire {
|
|||
}
|
||||
|
||||
model StarSystem {
|
||||
id String @id @default(uuid())
|
||||
universeId String
|
||||
index Int
|
||||
universeId Int
|
||||
universe Universe @relation(fields: [universeId], references: [id])
|
||||
|
||||
genIndex Int
|
||||
coordX Float
|
||||
coordY Float
|
||||
|
||||
inbound StarSystemEdges[] @relation("inbound")
|
||||
outbound StarSystemEdges[] @relation("outbound")
|
||||
|
||||
@@id([universeId, index])
|
||||
}
|
||||
|
||||
model StarSystemEdges {
|
||||
universeId String
|
||||
universeId Int
|
||||
universe Universe @relation(fields: [universeId], references: [id])
|
||||
|
||||
fromSystemId String
|
||||
from StarSystem @relation(fields: [fromSystemId], references: [id], name: "outbound")
|
||||
fromIndex Int
|
||||
fromSystemId Int
|
||||
from StarSystem @relation(fields: [universeId, fromSystemId], references: [universeId, index], name: "outbound")
|
||||
|
||||
toSystemId String
|
||||
to StarSystem @relation(fields: [toSystemId], references: [id], name: "inbound")
|
||||
toIndex Int
|
||||
toSystemId Int
|
||||
to StarSystem @relation(fields: [universeId, toSystemId], references: [universeId, index], name: "inbound")
|
||||
|
||||
@@id([universeId, fromSystemId, toSystemId])
|
||||
}
|
||||
|
@ -85,14 +84,14 @@ model StarSystemEdges {
|
|||
model Planet {
|
||||
id String @id @default(uuid())
|
||||
|
||||
universeId String
|
||||
universeId Int
|
||||
universe Universe @relation(fields: [universeId], references: [id])
|
||||
}
|
||||
|
||||
model Fleet {
|
||||
id String @id @default(uuid())
|
||||
|
||||
universeId String
|
||||
universeId Int
|
||||
universe Universe @relation(fields: [universeId], references: [id])
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue