start working on RFC#11,

implement a regionmanager and basic region component as well as tests for those
this is a fist implementation and likly to change

Former-commit-id: fdb7097dc30ab1642d25a02532458bcc2811ab61
This commit is contained in:
Marcel Märtens 2019-04-30 01:10:33 +02:00
parent add74cd0ea
commit 1456497bd0
18 changed files with 1211 additions and 107 deletions

558
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ members = [
"world",
"network",
"network/protocol",
"worldsim"
]
# default profile for devs, fast to compile, okay enough to run, no debug information

1
assets/voxygen/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
tmp/

View File

@ -28,6 +28,7 @@ common = { package = "veloren-common", path = "../common" }
common-base = { package = "veloren-common-base", path = "../common/base" }
common-net = { package = "veloren-common-net", path = "../common/net" }
common-frontend = { package = "veloren-common-frontend", path = "../common/frontend" }
worldsim = { package = "veloren-worldsim", path = "../worldsim" }
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
num_cpus = "1.0"

View File

@ -27,9 +27,17 @@ use std::{
use structopt::StructOpt;
use tracing::{info, trace};
use worldsim::{
regionmanager::{RegionManager, meta::RegionManagerMsg},
server::meta::{ServerMsg},
job::JobManager,
region::Region,
};
lazy_static::lazy_static! {
pub static ref LOG: TuiLog<'static> = TuiLog::default();
}
const TPS: u64 = 30;
fn main() -> io::Result<()> {
@ -155,6 +163,18 @@ fn main() -> io::Result<()> {
let server_port = &server_settings.gameserver_address.port();
let metrics_port = &server_settings.metrics_address.port();
let (region_manager_tx, region_manager_rx) = mpsc::channel::<RegionManagerMsg>();
let (server_tx, server_rx) = mpsc::channel::<ServerMsg>();
let mut region_manager = RegionManager::new(region_manager_tx, server_rx);
let mut job_manager: Arc<JobManager> = Arc::new(JobManager::new());
let mut server = worldsim::server::Server::new(server_tx,region_manager_rx,job_manager.clone());
let mut region = Region::new((0,0),job_manager.clone());
job_manager.repeat(move || region_manager.work() );
job_manager.repeat(move || server.work() );
// Create server
let mut server = Server::new(
server_settings,

21
worldsim/Cargo.toml Normal file
View File

@ -0,0 +1,21 @@
[package]
name = "veloren-worldsim"
version = "0.1.0"
authors = ["Marcel Märtens <marcel.cochem@googlemail.com>"]
edition = "2018"
[dependencies]
sphynx = { git = "https://gitlab.com/veloren/sphynx.git", features = ["serde1"] }
specs = { version = "0.14", features = ["serde"] }
shred = { version = "0.7", features = ["nightly"] }
vek = { version = "0.9", features = ["serde"] }
dot_vox = "4.0"
threadpool = "1.7"
mio = "0.6"
mio-extras = "2.0"
serde = "1.0"
serde_derive = "1.0"
bincode = "1.0"
log = "0.4"
rand = "0.5"

22
worldsim/src/job/mod.rs Normal file
View File

@ -0,0 +1,22 @@
use std::thread;
#[derive(Debug, Clone)]
/*only have one JobManager per System because it bounds to all threads*/
pub struct JobManager {
}
impl JobManager{
pub fn new() -> Self {
Self {
}
}
pub fn repeat<F>(&self, mut f: F) where F: FnMut() -> bool, F: Send + 'static {
let worker = thread::spawn(move || {
while f() {
}
});
}
}

19
worldsim/src/lib.rs Normal file
View File

@ -0,0 +1,19 @@
#![feature(euclidean_division, duration_float, trait_alias, bind_by_move_pattern_guards)]
extern crate serde_derive;
#[macro_use]
extern crate log;
pub mod job;
pub mod regionmanager;
pub mod region;
pub mod server;
pub mod lodstore;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

View File

@ -0,0 +1,47 @@
use vek::*;
/*
For our LodStructures we need a type that covers the values from 0 - 2047 in steps of 1/32.
which is 11 bits for the digits before the decimal point and 5 bits for the digits after the decimal point.
Because for accessing the decimal point makes no difference we use a u16 to represent this value.
The value needs to be shiftet to get it's "real inworld size",
e.g. 1 represents 1/32
32 represents 1
65535 represents 2047 + 31/32
*/
pub type LodIndex = Vec3<u16>;
pub fn to_lod_i(pos: Vec3<u16>) -> LodIndex {
pos.map(|x| x * 32)
}
/*will round*/
pub fn to_lod_f(pos: Vec3<f32>) -> LodIndex {
pos.map(|x| (x * 32.0).round() as u16)
}
pub fn to_pos_i(index: LodIndex) -> Vec3<u16> {
index.map(|x| x / 32)
}
pub fn to_pos_f(index: LodIndex) -> Vec3<f32> {
index.map(|x| x as f32 / 32.0)
}
pub const LEVEL_LENGTH_POW_MAX: i8 = 11;
pub const LEVEL_LENGTH_POW_MIN: i8 = -4;
pub const LEVEL_INDEX_POW_MAX: u8 = 15;
pub const LEVEL_INDEX_POW_MIN: u8 = 0;
pub const fn length_to_index(n: i8) -> u8 { (n+4) as u8 }
pub const fn two_pow_u(n: u8) -> u16 {
1 << n
}
pub fn two_pow_i(n: i8) -> f32 {
2.0_f32.powi(n as i32)
}

View File

@ -0,0 +1,135 @@
pub mod index;
use std::sync::Arc;
use vek::*;
use index::{
LodIndex,
length_to_index,
two_pow_u,
};
/*
Alternative impl idea:
1) Put LodLayer to a trait to give everyone the power to store it themself.
2) Put childs in up to 8 different VECs, and have a index in LogLayer, pointing to the fist childred in this supervector, and ge the length from E.
all access is only possible if the Owner sturcture is given
*/
#[derive(Debug, Clone)]
pub struct LodLayer<E> {
pub data: E,
pub childs: Vec<LodLayer<E>>, //Optimization potential: size_of<Vec> == 24 and last layer doesnt need Vec at all.
}
pub trait Layer: Sized {
fn new() -> LodLayer<Self>;
fn get_level(layer: &LodLayer<Self>) -> i8;
fn get_lower_level(layer: &LodLayer<Self>) -> Option<i8>;
/*Drills down the layer and creates childs*/
fn drill_down(layer: &mut LodLayer<Self>);
/*needs to recalc parent values and remove childs*/
fn drill_up(parent: &mut LodLayer<Self>);
}
impl<E> LodLayer<E> {
pub fn new_data(data: E) -> Self {
Self {
data,
childs: vec!(),
}
}
}
impl<E: Layer> LodLayer<E> {
// gets the internal index on this layer from relative position
fn get_internal_index(&self, relative: LodIndex) -> Vec3<u16> {
let ll = length_to_index(E::get_lower_level(self).expect("your type is wrong configured!, configure Layer trait correctly"));
let length_per_children: u16 = two_pow_u(ll);
let child_index = relative.map(|i| (i / length_per_children));
return child_index;
}
fn get_internal_index_and_remainder(&self, relative: LodIndex) -> (Vec3<u16>, LodIndex) {
let ll = length_to_index(E::get_lower_level(self).expect("your type is wrong configured!, configure Layer trait correctly"));
let length_per_children: u16 = two_pow_u(ll);
let child_index = relative.map(|i| (i / length_per_children));
let remainder_index = relative.map2(child_index, |i,c| (i - c * length_per_children));
return (child_index, remainder_index);
}
/*flatten the (1,2,3) child to 1*4+2*4+3*3*4 = 48*/
fn get_flat_index(&self, internal_index: Vec3<u16>) -> usize {
let ll = E::get_lower_level(self).expect("your type is wrong configured!, configure Layer trait correctly");
let cl = E::get_level(self);
let childs_per_dimentsion = (cl - ll) as usize;
let index = internal_index.x as usize + internal_index.y as usize * childs_per_dimentsion + internal_index.z as usize * childs_per_dimentsion * childs_per_dimentsion;
return index;
}
//index must be local to self
fn get(&self, relative: LodIndex) -> &LodLayer<E> {
// index is local for now
if self.childs.is_empty() {
return &self
} else {
let (int, rem) = self.get_internal_index_and_remainder(relative);
let index = self.get_flat_index(int);
&self.childs.get(index).unwrap().get(rem)
}
}
/*
These functions allow you to make the LodLayer provide a certain LOD for the specified area
*/
/*is at least minimum or maximum*/
pub fn make_at_least(&mut self, lower: LodIndex, upper: LodIndex, level: i8) {
if E::get_level(self) <= level {
//println!("done");
return;
}
let (li, lr) = self.get_internal_index_and_remainder(lower);
let (ui, ur) = self.get_internal_index_and_remainder(upper);
if self.childs.is_empty() {
E::drill_down(self);
//println!("dd");
}
let ll = length_to_index(E::get_lower_level(self).expect("your type is wrong configured!, configure Layer trait correctly"));
let length_per_children: u16 = two_pow_u(ll);
//println!("li {} lr {} ui {} ur {} length {}", li, lr, ui, ur, length_per_children);
if E::get_lower_level(self).unwrap() <= level {
//println!("done");
return;
}
for z in li.z..ui.z+1 {
for y in li.y..ui.y+1 {
for x in li.x..ui.x+1 {
//recursive call make_at_least
let mut new_lower = LodIndex::new(0,0,0);
let mut new_upper = LodIndex::new(length_per_children-1,length_per_children-1,length_per_children-1);
if x == li.x { new_lower.x = lr.x; }
if y == li.y { new_lower.y = lr.y; }
if z == li.z { new_lower.z = lr.z; }
if x == ui.x { new_upper.x = ur.x; }
if y == ui.y { new_upper.y = ur.y; }
if z == ui.z { new_upper.z = ur.z; }
let index = self.get_flat_index(LodIndex::new(x,y,z));
//println!("lo {} up {} layer {} inr {} in {}, vec {}", new_lower, new_upper, E::get_level(self), LodIndex::new(x,y,z), index, self.childs.len());
self.childs[index].make_at_least( new_lower, new_upper, level );
}
}
}
}
fn make_at_most(&mut self, lower: LodIndex, upper: LodIndex, level: i8) {
}
fn make_exactly(&mut self, lower: LodIndex, upper: LodIndex, level: i8) {
}
}

View File

@ -0,0 +1 @@
pub mod terrain;

View File

@ -0,0 +1,144 @@
use crate::lodstore::Layer;
use crate::lodstore::LodLayer;
#[derive(Debug, Clone)]
pub enum Terrain {
// 11 is max
Unused11,
Region9 { //512m this is for normal simulation if no player nearby
precent_air: f32,
percent_forrest: f32,
percent_lava: f32,
percent_water: f32,
},
Chunk5 {//32m, same detail as region, but to not force block1 everywhere in 512 area
precent_air: f32,
percent_forrest: f32,
percent_lava: f32,
percent_water: f32,
},
Block1 {
material: u32,
},
SubBlock_4 {
material: u32,
},
// -4 is min
}
impl Terrain {
fn new() -> Self {
Terrain::Unused11
}
}
const LAYER5: i8 = 11;
const LAYER4: i8 = 9;
const LAYER3: i8 = 5;
const LAYER2: i8 = 0;
const LAYER1: i8 = -4;
impl Layer for Terrain {
fn new() -> LodLayer<Terrain> {
let mut n = LodLayer::<Terrain>::new_data(Terrain::Unused11);
Self::drill_down(&mut n);
n
}
fn get_level(layer: &LodLayer<Self>) -> i8 {
match &layer.data {
Terrain::Unused11 => LAYER5,
Terrain::Region9{..} => LAYER4,
Terrain::Chunk5{..} => LAYER3,
Terrain::Block1{..} => LAYER2,
Terrain::SubBlock_4{..} => -LAYER1,
}
}
fn get_lower_level(layer: &LodLayer<Self>) -> Option<i8> {
match &layer.data {
Terrain::Unused11 => Some(LAYER4),
Terrain::Region9{..} => Some(LAYER3),
Terrain::Chunk5{..} => Some(LAYER2),
Terrain::Block1{..} => Some(LAYER1),
Terrain::SubBlock_4{..} => None,
}
}
fn drill_down(layer: &mut LodLayer<Terrain>) {
match &layer.data {
Terrain::Unused11 => {
let n = LodLayer::new_data(Terrain::Region9{
precent_air: 1.0,
percent_forrest: 0.0,
percent_lava: 0.0,
percent_water: 0.0,
});
layer.childs = vec![n; 2_usize.pow((LAYER5-LAYER4) as u32 *3)];
},
Terrain::Region9{..} => {
let n = LodLayer::new_data(Terrain::Chunk5{
precent_air: 1.0,
percent_forrest: 0.0,
percent_lava: 0.0,
percent_water: 0.0,
});
layer.childs = vec![n; 2_usize.pow((LAYER4-LAYER3) as u32 *3)];
},
Terrain::Chunk5{..} => {
let n = LodLayer::new_data( Terrain::Block1{
material: 10,
});
layer.childs = vec![n; 2_usize.pow((LAYER3-LAYER2) as u32 *3)];
},
Terrain::Block1{..} => {
let n = LodLayer::new_data( Terrain::SubBlock_4{
material: 10,
});
layer.childs = vec![n; 2_usize.pow((LAYER2-LAYER1) as u32 *3)];
},
Terrain::SubBlock_4{..} => {
panic!("cannot drillDown further")
},
}
}
fn drill_up(parent: &mut LodLayer<Terrain>) {
match &parent.data {
Terrain::Unused11 => {
panic!("cannot drillUp further")
},
Terrain::Region9{..} => {
//recalculate values here
parent.data = Terrain::Region9{
precent_air: 1.0,
percent_forrest: 0.0,
percent_lava: 0.0,
percent_water: 0.0,
};
parent.childs = vec![];
},
Terrain::Chunk5{..} => {
parent.data = Terrain::Chunk5{
precent_air: 1.0,
percent_forrest: 0.0,
percent_lava: 0.0,
percent_water: 0.0,
};
parent.childs = vec![];
},
Terrain::Block1{..} => {
parent.data = Terrain::Block1{
material: 10,
};
parent.childs = vec![];
},
Terrain::SubBlock_4{..} => {
parent.data = Terrain::SubBlock_4{
material: 10,
};
parent.childs = vec![];
},
}
}
}

View File

@ -0,0 +1,14 @@
use crate::regionmanager::meta::RegionId;
#[derive(Debug, Clone)]
pub struct RegionMeta {
id: RegionId,
}
impl RegionMeta {
pub fn new(id: RegionId) -> Self {
Self {
id,
}
}
}

View File

@ -0,0 +1,87 @@
pub mod meta;
mod lod;
use std::sync::Arc;
use crate::{
regionmanager::meta::RegionId,
job::JobManager,
lodstore::LodLayer,
lodstore::Layer,
};
use lod::terrain::Terrain;
#[derive(Debug, Clone)]
pub struct Region {
id: RegionId,
jobmanager: Arc<JobManager>,
pub block: LodLayer<Terrain>,
temp: LodLayer<Terrain>,
light: LodLayer<Terrain>,
evil: LodLayer<Terrain>,
civ: LodLayer<Terrain>,
}
impl Region {
pub fn new(id: RegionId, jobmanager: Arc<JobManager>) -> Self {
Self {
id,
jobmanager,
block: Terrain::new(),
temp: Terrain::new(),
light: Terrain::new(),
evil: Terrain::new(),
civ: Terrain::new(),
}
}
}
/*
pub type aaa = LodLayer<e::Terain>;
fn example() {
let own = e::Terain::new();
let t8 = own.get(Vec3::new(1,1,1));
//let tn = own.get2((1,2,3))
}
*/
#[cfg(test)]
mod tests {
use crate::{
regionmanager::meta::RegionId,
job::JobManager,
lodstore::LodLayer,
lodstore::Layer,
region::lod::terrain::Terrain,
region::Region,
};
use vek::*;
use std::sync::Arc;
use std::{thread, time};
/*
#[test]
fn createRegion() {
let mut r = Region::new((0,0), Arc::new(JobManager::new()));
r.block.make_at_least(Vec3::new(0,0,0), Vec3::new(65535,65535,65535), 9);
}*/
#[test]
fn createRegionToBlock() {
// one region fully blown needs around 80 GB, 1/8 of a region needs 10GB for full block level
let mut r = Region::new((0,0), Arc::new(JobManager::new()));
r.block.make_at_least(Vec3::new(0,0,0), Vec3::new(65535/2,65535/2,65535/2), 0);
r.block.make_at_least(Vec3::new(0,0,0), Vec3::new(65535/2,65535/2,65535/2), 0);
thread::sleep(time::Duration::from_secs(100));
}
/*
#[test]
fn createRegionToSubBlock() {
let mut r = Region::new((0,0), Arc::new(JobManager::new()));
r.block.make_at_least(Vec3::new(0,0,0), Vec3::new(65535,65535,65535), -4);
}*/
}

View File

@ -0,0 +1,43 @@
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct Server {
connection_details: String,
}
impl Server {
pub fn new(connection_details: String) -> Self {
Self {
connection_details,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum RegionManagerMsg {
Attached{server_id: u64, seed: u64},
NewServerInMesh{server_id: u64, server_connection_details: ()},
CreateRegion{region_id: RegionId},
TakeOverRegionFrom{region_id: RegionId, server_id: u64},
}
pub type RegionIdSize = i8;
pub type RegionId = (/*x*/RegionIdSize, /*y*/RegionIdSize /*z = 0*/);
pub const RegionMIN:i8 = -64;
pub const RegionMAX:i8 = 63;
#[derive(Debug, Clone)]
pub struct Region {
pub tick_time: Duration,
pub server_id: Option<u8>,
}
impl Region {
pub fn new(server_id: Option<u8>) -> Self {
Self {
tick_time: Duration::from_millis(0),
server_id,
}
}
}

View File

@ -0,0 +1,100 @@
pub mod meta;
use std::sync::{
Arc,
mpsc,
atomic::{AtomicBool, Ordering},
};
use std::thread::sleep;
use std::collections::HashMap;
use std::time::Duration;
use crate::{
regionmanager::meta::{
Server, Region, RegionId, RegionManagerMsg, RegionMIN, RegionMAX,
},
server::meta::{
ServerMsg,
},
job::JobManager,
};
#[derive(Debug)]
pub struct RegionManager {
tx: mpsc::Sender<RegionManagerMsg>,
rx: mpsc::Receiver<ServerMsg>,
running: Arc<AtomicBool>,
servers: Vec<Server>,
regions: HashMap<RegionId, Region>,
}
impl RegionManager{
pub fn new(tx: mpsc::Sender<RegionManagerMsg>, rx: mpsc::Receiver<ServerMsg>) -> Self {
let running = Arc::new(AtomicBool::new(true));
let mut servers = vec!();
let mut regions = HashMap::new();
for x in RegionMIN..RegionMAX {
for y in RegionMIN..RegionMAX {
regions.insert((x,y), Region::new(None));
}
}
Self {
tx,
rx,
running,
servers,
regions,
}
}
pub fn rearange(&mut self) {
//This is a super intelligent algorithm which says which chunks should be handled by which server
//It is widely important, that it causes as minimal shifting as necessary
//.... fell f*** it for now
for x in RegionMIN..RegionMAX {
for y in RegionMIN..RegionMAX {
if !self.servers.is_empty() {
let old = self.regions.get(&(x,y)).unwrap().server_id;
self.regions.get_mut(&(x,y)).unwrap().server_id = Some(((x as usize) % self.servers.len()) as u8);
if let Some(id) = old {
self.tx.send(RegionManagerMsg::TakeOverRegionFrom{region_id: (x,y), server_id: id as u64});
} else {
self.tx.send(RegionManagerMsg::CreateRegion{region_id: (x,y)});
}
} else {
self.regions.get_mut(&(x,y)).unwrap().server_id = None;
}
}
}
}
pub fn work(
&mut self,
//jm: &JobManager,
) -> bool {
match self.rx.try_recv() {
Ok(msg) => {
match msg {
ServerMsg::Attach() => {
//ERROR i cannot acceess self here ...
self.servers.push(Server::new("Hello".to_string()) );
self.tx.send(RegionManagerMsg::Attached{server_id: self.servers.len() as u64 , seed: 1337});
error!("yay");
println!("attached");
self.rearange();
}
}
},
Err(e) => {
//panic!("Work error {:?}", e);
}
}
sleep(Duration::from_millis(10));
self.running.load(Ordering::Relaxed)
}
}

View File

@ -0,0 +1,10 @@
#[derive(Debug, Clone, Copy)]
pub enum ServerMsg {
Attach(),
}
pub type RegionIdSize = i8;
pub type RegionId = (/*x*/RegionIdSize, /*y*/RegionIdSize /*z = 0*/);
pub const RegionMIN:i8 = -64;
pub const RegionMAX:i8 = 63;

View File

@ -0,0 +1,94 @@
pub mod meta;
use std::sync::{
mpsc,
Arc,
atomic::{AtomicBool, Ordering},
};
use std::thread::sleep;
use std::time::Duration;
use std::collections::HashMap;
use vek::*;
use crate::{
job::JobManager,
regionmanager::meta::RegionManagerMsg,
region::Region,
server::meta::RegionId,
server::meta::ServerMsg,
};
/*
one server per physical host
*/
#[derive(Debug)]
pub struct Server {
tx: mpsc::Sender<ServerMsg>,
rx: mpsc::Receiver<RegionManagerMsg>,
running: Arc<AtomicBool>,
id: Option<u64>,
seed: Option<u64>,
state: u64,
jobmanager: Arc<JobManager>,
region: HashMap<RegionId,Region>,
}
impl Server {
pub fn new(tx: mpsc::Sender<ServerMsg>, rx: mpsc::Receiver<RegionManagerMsg>, jobmanager: Arc<JobManager>) -> Self {
let running = Arc::new(AtomicBool::new(true));
Self {
tx,
rx,
running,
id: None,
seed: None,
state: 0,
jobmanager: jobmanager.clone(),
region: HashMap::new(),
}
}
pub fn work(
&mut self,
//jm: &JobManager,
) -> bool {
match self.state {
0 => {
self.tx.send(ServerMsg::Attach());
self.state += 1;
},
_ => (),
}
match self.rx.try_recv() {
Ok(msg) => {
match msg {
RegionManagerMsg::Attached{server_id, seed} => {
self.id = Some(server_id);
self.seed = Some(seed);
},
RegionManagerMsg::NewServerInMesh{server_id, server_connection_details} => {
println!("new server found");
},
RegionManagerMsg::CreateRegion{region_id} => {
let mut r = Region::new(region_id, self.jobmanager.clone());
r.block.make_at_least(Vec3::new(0,0,0), Vec3::new(65535,65535,65535), 9);
self.region.insert(region_id, r);
println!("create region");
},
RegionManagerMsg::TakeOverRegionFrom{region_id, server_id} => {
println!("new server in mesh");
},
_ => (),
}
},
Err(e) => {
//panic!("Work error {:?}", e);
sleep(Duration::from_millis(10));
}
}
self.running.load(Ordering::Relaxed)
}
}