mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Started work on server-side chunks
Former-commit-id: 84a6bd7358f67a77043c4b11c787538f073c8d28
This commit is contained in:
parent
11630877e3
commit
3d9f8105e6
@ -6,7 +6,6 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
world = { package = "veloren-world", path = "../world" }
|
||||
|
||||
specs = "0.14"
|
||||
vek = "0.9"
|
||||
|
@ -15,7 +15,7 @@ use std::{
|
||||
net::SocketAddr,
|
||||
};
|
||||
use vek::*;
|
||||
use threadpool;
|
||||
use threadpool::ThreadPool;
|
||||
use specs::Builder;
|
||||
use common::{
|
||||
comp,
|
||||
@ -24,7 +24,6 @@ use common::{
|
||||
net::PostBox,
|
||||
msg::{ClientMsg, ServerMsg},
|
||||
};
|
||||
use world::World;
|
||||
|
||||
const SERVER_TIMEOUT: f64 = 5.0; // Seconds
|
||||
|
||||
@ -33,7 +32,7 @@ pub enum Event {
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
thread_pool: threadpool::ThreadPool,
|
||||
thread_pool: ThreadPool,
|
||||
|
||||
last_ping: f64,
|
||||
postbox: PostBox<ClientMsg, ServerMsg>,
|
||||
@ -41,10 +40,7 @@ pub struct Client {
|
||||
tick: u64,
|
||||
state: State,
|
||||
player: Option<EcsEntity>,
|
||||
|
||||
// Testing
|
||||
world: World,
|
||||
pub chunk: Option<TerrainChunk>,
|
||||
view_distance: u64,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
@ -54,6 +50,7 @@ impl Client {
|
||||
addr: A,
|
||||
player: comp::Player,
|
||||
character: Option<comp::Character>,
|
||||
view_distance: u64,
|
||||
) -> Result<Self, Error> {
|
||||
|
||||
let mut postbox = PostBox::to_server(addr)?;
|
||||
@ -85,10 +82,7 @@ impl Client {
|
||||
tick: 0,
|
||||
state,
|
||||
player,
|
||||
|
||||
// Testing
|
||||
world: World::new(),
|
||||
chunk: None,
|
||||
view_distance,
|
||||
})
|
||||
}
|
||||
|
||||
@ -204,6 +198,7 @@ impl Client {
|
||||
ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)),
|
||||
ServerMsg::SetPlayerEntity(uid) => self.player = Some(self.state.ecs().entity_from_uid(uid).unwrap()), // TODO: Don't unwrap here!
|
||||
ServerMsg::EcsSync(sync_package) => self.state.ecs_mut().sync_with_package(sync_package),
|
||||
ServerMsg::TerrainChunkUpdate { key, chunk } => self.state.insert_chunk(key, chunk),
|
||||
}
|
||||
}
|
||||
} else if let Some(err) = self.postbox.error() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::comp;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum ClientMsg {
|
||||
Connect {
|
||||
player: comp::Player,
|
||||
|
@ -1,6 +1,8 @@
|
||||
use vek::*;
|
||||
use crate::terrain::TerrainChunk;
|
||||
use super::EcsPacket;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum ServerMsg {
|
||||
Handshake {
|
||||
ecs_state: sphynx::StatePackage<EcsPacket>,
|
||||
@ -12,4 +14,8 @@ pub enum ServerMsg {
|
||||
Chat(String),
|
||||
SetPlayerEntity(u64),
|
||||
EcsSync(sphynx::SyncPackage<EcsPacket>),
|
||||
TerrainChunkUpdate {
|
||||
key: Vec3<i32>,
|
||||
chunk: TerrainChunk,
|
||||
},
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ impl<T> From<mio_extras::channel::SendError<T>> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PostSend = 'static + serde::Serialize + Send + fmt::Debug;
|
||||
pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send + fmt::Debug;
|
||||
pub trait PostSend = 'static + serde::Serialize + Send;
|
||||
pub trait PostRecv = 'static + serde::de::DeserializeOwned + Send;
|
||||
|
||||
const TCP_TOK: Token = Token(0);
|
||||
const CTRL_TOK: Token = Token(1);
|
||||
|
@ -1,4 +1,7 @@
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
time::Duration,
|
||||
collections::HashSet,
|
||||
};
|
||||
use shred::{Fetch, FetchMut};
|
||||
use specs::{
|
||||
Builder,
|
||||
@ -17,7 +20,10 @@ use vek::*;
|
||||
use crate::{
|
||||
comp,
|
||||
sys,
|
||||
terrain::TerrainMap,
|
||||
terrain::{
|
||||
TerrainMap,
|
||||
TerrainChunk,
|
||||
},
|
||||
msg::EcsPacket,
|
||||
};
|
||||
|
||||
@ -36,17 +42,17 @@ struct Time(f64);
|
||||
pub struct DeltaTime(pub f64);
|
||||
|
||||
pub struct Changes {
|
||||
pub new_chunks: Vec<Vec3<i32>>,
|
||||
pub changed_chunks: Vec<Vec3<i32>>,
|
||||
pub removed_chunks: Vec<Vec3<i32>>,
|
||||
pub new_chunks: HashSet<Vec3<i32>>,
|
||||
pub changed_chunks: HashSet<Vec3<i32>>,
|
||||
pub removed_chunks: HashSet<Vec3<i32>>,
|
||||
}
|
||||
|
||||
impl Changes {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
new_chunks: vec![],
|
||||
changed_chunks: vec![],
|
||||
removed_chunks: vec![],
|
||||
new_chunks: HashSet::new(),
|
||||
changed_chunks: HashSet::new(),
|
||||
removed_chunks: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,12 +158,23 @@ impl State {
|
||||
|
||||
/// Get a reference to this state's terrain.
|
||||
pub fn terrain(&self) -> Fetch<TerrainMap> {
|
||||
self.ecs.internal().read_resource::<TerrainMap>()
|
||||
self.ecs
|
||||
.internal()
|
||||
.read_resource::<TerrainMap>()
|
||||
}
|
||||
|
||||
// TODO: Get rid of this since it shouldn't be needed
|
||||
pub fn terrain_mut(&mut self) -> FetchMut<TerrainMap> {
|
||||
self.ecs.internal_mut().write_resource::<TerrainMap>()
|
||||
/// Insert the provided chunk into this state's terrain.
|
||||
pub fn insert_chunk(&mut self, key: Vec3<i32>, chunk: TerrainChunk) {
|
||||
if self.ecs
|
||||
.internal_mut()
|
||||
.write_resource::<TerrainMap>()
|
||||
.insert(key, chunk)
|
||||
.is_some()
|
||||
{
|
||||
self.changes.changed_chunks.insert(key);
|
||||
} else {
|
||||
self.changes.new_chunks.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a single tick, simulating the game state by the given duration.
|
||||
|
@ -1,3 +1,6 @@
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum BiomeKind {
|
||||
Void,
|
||||
Grassland,
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Library
|
||||
use vek::*;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
// Crate
|
||||
use crate::vol::Vox;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Block {
|
||||
kind: u8,
|
||||
color: [u8; 3],
|
||||
|
@ -7,10 +7,8 @@ pub use self::{
|
||||
biome::BiomeKind,
|
||||
};
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use crate::{
|
||||
vol::VolSize,
|
||||
volumes::{
|
||||
@ -21,6 +19,7 @@ use crate::{
|
||||
|
||||
// TerrainChunkSize
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct TerrainChunkSize;
|
||||
|
||||
impl VolSize for TerrainChunkSize {
|
||||
@ -29,6 +28,7 @@ impl VolSize for TerrainChunkSize {
|
||||
|
||||
// TerrainChunkMeta
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct TerrainChunkMeta {
|
||||
biome: BiomeKind,
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use std::marker::PhantomData;
|
||||
|
||||
// Library
|
||||
use vek::*;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
// Local
|
||||
use crate::vol::{
|
||||
@ -23,6 +24,7 @@ pub enum ChunkErr {
|
||||
// V = Voxel
|
||||
// S = Size (replace when const generics are a thing)
|
||||
// M = Metadata
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Chunk<V: Vox, S: VolSize, M> {
|
||||
vox: Vec<V>,
|
||||
meta: M,
|
||||
|
@ -129,4 +129,8 @@ impl<V: Vox, S: VolSize, M> VolMap<V, S, M> {
|
||||
pub fn remove(&mut self, key: &Vec3<i32>) -> Option<Chunk<V, S, M>> {
|
||||
self.chunks.remove(key)
|
||||
}
|
||||
|
||||
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> {
|
||||
key * S::SIZE.map(|e| e as i32)
|
||||
}
|
||||
}
|
||||
|
@ -10,3 +10,4 @@ world = { package = "veloren-world", path = "../world" }
|
||||
|
||||
specs = "0.14"
|
||||
vek = "0.9"
|
||||
threadpool = "1.7"
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use specs::Entity as EcsEntity;
|
||||
use common::{
|
||||
comp,
|
||||
@ -14,7 +15,6 @@ pub enum ClientState {
|
||||
|
||||
pub struct Client {
|
||||
pub state: ClientState,
|
||||
pub entity: EcsEntity,
|
||||
pub postbox: PostBox<ServerMsg, ClientMsg>,
|
||||
pub last_ping: f64,
|
||||
}
|
||||
@ -26,36 +26,42 @@ impl Client {
|
||||
}
|
||||
|
||||
pub struct Clients {
|
||||
clients: Vec<Client>,
|
||||
clients: HashMap<EcsEntity, Client>,
|
||||
}
|
||||
|
||||
impl Clients {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
clients: Vec::new(),
|
||||
clients: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, client: Client) {
|
||||
self.clients.push(client);
|
||||
pub fn add(&mut self, entity: EcsEntity, client: Client) {
|
||||
self.clients.insert(entity, client);
|
||||
}
|
||||
|
||||
pub fn remove_if<F: FnMut(&mut Client) -> bool>(&mut self, f: F) {
|
||||
self.clients.drain_filter(f);
|
||||
pub fn remove_if<F: FnMut(EcsEntity, &mut Client) -> bool>(&mut self, mut f: F) {
|
||||
self.clients.retain(|entity, client| !f(*entity, client));
|
||||
}
|
||||
|
||||
pub fn notify(&mut self, entity: EcsEntity, msg: ServerMsg) {
|
||||
if let Some(client) = self.clients.get_mut(&entity) {
|
||||
client.notify(msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_connected(&mut self, msg: ServerMsg) {
|
||||
for client in &mut self.clients {
|
||||
for client in self.clients.values_mut() {
|
||||
if client.state == ClientState::Connected {
|
||||
client.postbox.send(msg.clone());
|
||||
client.notify(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_connected_except(&mut self, entity: EcsEntity, msg: ServerMsg) {
|
||||
for client in &mut self.clients {
|
||||
if client.entity != entity && client.state == ClientState::Connected {
|
||||
client.postbox.send(msg.clone());
|
||||
pub fn notify_connected_except(&mut self, except_entity: EcsEntity, msg: ServerMsg) {
|
||||
for (entity, client) in self.clients.iter_mut() {
|
||||
if client.state == ClientState::Connected && *entity != except_entity {
|
||||
client.notify(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ pub use crate::{
|
||||
use std::{
|
||||
time::Duration,
|
||||
net::SocketAddr,
|
||||
sync::mpsc,
|
||||
};
|
||||
use specs::{
|
||||
Entity as EcsEntity,
|
||||
@ -22,11 +23,13 @@ use specs::{
|
||||
saveload::MarkedBuilder,
|
||||
};
|
||||
use vek::*;
|
||||
use threadpool::ThreadPool;
|
||||
use common::{
|
||||
comp,
|
||||
state::State,
|
||||
net::PostOffice,
|
||||
msg::{ServerMsg, ClientMsg},
|
||||
terrain::TerrainChunk,
|
||||
};
|
||||
use world::World;
|
||||
use crate::client::{
|
||||
@ -56,18 +59,30 @@ pub struct Server {
|
||||
|
||||
postoffice: PostOffice<ServerMsg, ClientMsg>,
|
||||
clients: Clients,
|
||||
|
||||
thread_pool: ThreadPool,
|
||||
chunk_tx: mpsc::Sender<(Vec3<i32>, TerrainChunk)>,
|
||||
chunk_rx: mpsc::Receiver<(Vec3<i32>, TerrainChunk)>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
/// Create a new `Server`.
|
||||
#[allow(dead_code)]
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let (chunk_tx, chunk_rx) = mpsc::channel();
|
||||
|
||||
Ok(Self {
|
||||
state: State::new(),
|
||||
world: World::new(),
|
||||
|
||||
postoffice: PostOffice::bind(SocketAddr::from(([0; 4], 59003)))?,
|
||||
clients: Clients::empty(),
|
||||
|
||||
thread_pool: threadpool::Builder::new()
|
||||
.thread_name("veloren-worker".into())
|
||||
.build(),
|
||||
chunk_tx,
|
||||
chunk_rx,
|
||||
})
|
||||
}
|
||||
|
||||
@ -119,6 +134,27 @@ impl Server {
|
||||
// Tick the client's LocalState (step 3)
|
||||
self.state.tick(dt);
|
||||
|
||||
// Fetch any generated `TerrainChunk`s and insert them into the terrain
|
||||
// Also, send the chunk data to anybody that is close by
|
||||
for (key, chunk) in self.chunk_rx.try_iter() {
|
||||
// Send the chunk to all nearby players
|
||||
for (entity, player, pos) in (
|
||||
&self.state.ecs().internal().entities(),
|
||||
&self.state.ecs().internal().read_storage::<comp::Player>(),
|
||||
&self.state.ecs().internal().read_storage::<comp::phys::Pos>(),
|
||||
).join() {
|
||||
// TODO: Distance check
|
||||
// if self.state.terrain().key_pos(key)
|
||||
|
||||
self.clients.notify(entity, ServerMsg::TerrainChunkUpdate {
|
||||
key,
|
||||
chunk: chunk.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
self.state.insert_chunk(key, chunk);
|
||||
}
|
||||
|
||||
// Synchronise clients with the new state of the world
|
||||
self.sync_clients();
|
||||
|
||||
@ -143,9 +179,8 @@ impl Server {
|
||||
.create_entity_synced()
|
||||
.build();
|
||||
|
||||
self.clients.add(Client {
|
||||
self.clients.add(entity, Client {
|
||||
state: ClientState::Connecting,
|
||||
entity,
|
||||
postbox,
|
||||
last_ping: self.state.get_time(),
|
||||
});
|
||||
@ -166,7 +201,7 @@ impl Server {
|
||||
let mut new_chat_msgs = Vec::new();
|
||||
let mut disconnected_clients = Vec::new();
|
||||
|
||||
self.clients.remove_if(|client| {
|
||||
self.clients.remove_if(|entity, client| {
|
||||
let mut disconnect = false;
|
||||
let new_msgs = client.postbox.new_messages();
|
||||
|
||||
@ -181,12 +216,12 @@ impl Server {
|
||||
ClientMsg::Connect { player, character } => {
|
||||
|
||||
// Write client components
|
||||
state.write_component(client.entity, player);
|
||||
state.write_component(client.entity, comp::phys::Pos(Vec3::zero()));
|
||||
state.write_component(client.entity, comp::phys::Vel(Vec3::zero()));
|
||||
state.write_component(client.entity, comp::phys::Dir(Vec3::unit_y()));
|
||||
state.write_component(entity, player);
|
||||
state.write_component(entity, comp::phys::Pos(Vec3::zero()));
|
||||
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
||||
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
||||
if let Some(character) = character {
|
||||
state.write_component(client.entity, character);
|
||||
state.write_component(entity, character);
|
||||
}
|
||||
|
||||
client.state = ClientState::Connected;
|
||||
@ -196,7 +231,7 @@ impl Server {
|
||||
ecs_state: state.ecs().gen_state_package(),
|
||||
player_entity: state
|
||||
.ecs()
|
||||
.uid_from_entity(client.entity)
|
||||
.uid_from_entity(entity)
|
||||
.unwrap()
|
||||
.into(),
|
||||
});
|
||||
@ -207,11 +242,11 @@ impl Server {
|
||||
ClientMsg::Connect { .. } => disconnect = true, // Not allowed when already connected
|
||||
ClientMsg::Ping => client.postbox.send(ServerMsg::Pong),
|
||||
ClientMsg::Pong => {},
|
||||
ClientMsg::Chat(msg) => new_chat_msgs.push((client.entity, msg)),
|
||||
ClientMsg::Chat(msg) => new_chat_msgs.push((entity, msg)),
|
||||
ClientMsg::PlayerPhysics { pos, vel, dir } => {
|
||||
state.write_component(client.entity, pos);
|
||||
state.write_component(client.entity, vel);
|
||||
state.write_component(client.entity, dir);
|
||||
state.write_component(entity, pos);
|
||||
state.write_component(entity, vel);
|
||||
state.write_component(entity, dir);
|
||||
},
|
||||
ClientMsg::Disconnect => disconnect = true,
|
||||
},
|
||||
@ -228,7 +263,7 @@ impl Server {
|
||||
}
|
||||
|
||||
if disconnect {
|
||||
disconnected_clients.push(client.entity);
|
||||
disconnected_clients.push(entity);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -58,7 +58,7 @@ impl PlayState for MainMenuState {
|
||||
|
||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||
|
||||
// Maintain the UI
|
||||
// Maintain the UI (TODO: Maybe clean this up a little to avoid rightward drift?)
|
||||
for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) {
|
||||
match event {
|
||||
MainMenuEvent::LoginAttempt{ username, server_address } => {
|
||||
@ -70,7 +70,7 @@ impl PlayState for MainMenuState {
|
||||
Ok(mut socket_adders) => {
|
||||
while let Some(socket_addr) = socket_adders.next() {
|
||||
// TODO: handle error
|
||||
match Client::new(socket_addr, comp::Player::new(username.clone()), Some(comp::Character::test())) {
|
||||
match Client::new(socket_addr, comp::Player::new(username.clone()), Some(comp::Character::test()), 300) {
|
||||
Ok(client) => {
|
||||
return PlayStateResult::Push(
|
||||
Box::new(CharSelectionState::new(
|
||||
|
Loading…
Reference in New Issue
Block a user