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",
|
"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",
|
||||||
|
|
|
@ -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
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]
|
#[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.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
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 empire;
|
||||||
mod fleet;
|
mod fleet;
|
||||||
mod game;
|
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 {}
|
pub struct StarSystemDerivedState {}
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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'}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
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 { 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;
|
||||||
|
|
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;
|
|
@ -1,3 +1,3 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
prisma_client_rust_cli::run();
|
prisma_client_rust_cli::run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue