mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'veloren-cleanup' into 'master'
change veloren code style See merge request veloren/veloren!657
This commit is contained in:
commit
5b46587c2d
17
.rustfmt.toml
Normal file
17
.rustfmt.toml
Normal file
@ -0,0 +1,17 @@
|
||||
hard_tabs = false
|
||||
version = "Two"
|
||||
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = true
|
||||
wrap_comments = true
|
||||
normalize_doc_attributes = true
|
||||
|
||||
use_try_shorthand = true
|
||||
reorder_impl_items = true
|
||||
|
||||
fn_single_line = true
|
||||
inline_attribute_width = 50
|
||||
match_block_trailing_comma = true
|
||||
merge_imports = true
|
||||
overflow_delimited_expr = true
|
||||
use_field_init_shorthand = true
|
@ -55,9 +55,11 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
thread::spawn(move || loop {
|
||||
let msg = read_input();
|
||||
tx.send(msg).unwrap();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
let msg = read_input();
|
||||
tx.send(msg).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
@ -74,13 +76,13 @@ fn main() {
|
||||
Err(err) => {
|
||||
error!("Error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
Event::Chat { message, .. } => println!("{}", message),
|
||||
Event::Disconnect => {} // TODO
|
||||
Event::Disconnect => {}, // TODO
|
||||
Event::DisconnectionNotification(time) => {
|
||||
let message = match time {
|
||||
0 => String::from("Goodbye!"),
|
||||
@ -88,7 +90,7 @@ fn main() {
|
||||
};
|
||||
|
||||
println!("{}", message)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,5 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<PostError> for Error {
|
||||
fn from(err: PostError) -> Self {
|
||||
Error::Network(err)
|
||||
}
|
||||
fn from(err: PostError) -> Self { Error::Network(err) }
|
||||
}
|
||||
|
@ -36,11 +36,13 @@ use std::{
|
||||
use uvth::{ThreadPool, ThreadPoolBuilder};
|
||||
use vek::*;
|
||||
|
||||
// The duration of network inactivity until the player is kicked to the main menu
|
||||
// @TODO in the future, this should be configurable on the server, and be provided to the client
|
||||
// The duration of network inactivity until the player is kicked
|
||||
// @TODO: in the future, this should be configurable on the server
|
||||
// and be provided to the client
|
||||
const SERVER_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
|
||||
// After this duration has elapsed, the user will begin getting kick warnings in their chat window
|
||||
// After this duration has elapsed, the user will begin getting kick warnings in
|
||||
// their chat window
|
||||
const SERVER_TIMEOUT_GRACE_PERIOD: Duration = Duration::from_secs(14);
|
||||
|
||||
pub enum Event {
|
||||
@ -93,7 +95,8 @@ impl Client {
|
||||
// TODO: Display that versions don't match in Voxygen
|
||||
if server_info.git_hash != common::util::GIT_HASH.to_string() {
|
||||
log::warn!(
|
||||
"Server is running {}[{}], you are running {}[{}], versions might be incompatible!",
|
||||
"Server is running {}[{}], you are running {}[{}], versions might be \
|
||||
incompatible!",
|
||||
server_info.git_hash,
|
||||
server_info.git_date,
|
||||
common::util::GIT_HASH.to_string(),
|
||||
@ -124,10 +127,10 @@ impl Client {
|
||||
log::debug!("Done preparing image...");
|
||||
|
||||
(state, entity, server_info, (world_map, map_size))
|
||||
}
|
||||
},
|
||||
Some(ServerMsg::Error(ServerError::TooManyPlayers)) => {
|
||||
return Err(Error::TooManyPlayers)
|
||||
}
|
||||
return Err(Error::TooManyPlayers);
|
||||
},
|
||||
_ => return Err(Error::ServerWentMad),
|
||||
};
|
||||
|
||||
@ -175,10 +178,10 @@ impl Client {
|
||||
loop {
|
||||
match self.postbox.next_message() {
|
||||
Some(ServerMsg::StateAnswer(Err((RequestStateError::Denied, _)))) => {
|
||||
break Err(Error::InvalidAuth)
|
||||
}
|
||||
break Err(Error::InvalidAuth);
|
||||
},
|
||||
Some(ServerMsg::StateAnswer(Ok(ClientState::Registered))) => break Ok(()),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +199,8 @@ impl Client {
|
||||
self.client_state = ClientState::Pending;
|
||||
}
|
||||
|
||||
/// Request a state transition to `ClientState::Registered` from an ingame state.
|
||||
/// Request a state transition to `ClientState::Registered` from an ingame
|
||||
/// state.
|
||||
pub fn request_remove_character(&mut self) {
|
||||
self.postbox.send_message(ClientMsg::ExitIngame);
|
||||
self.client_state = ClientState::Pending;
|
||||
@ -259,13 +263,9 @@ impl Client {
|
||||
.send_message(ClientMsg::ControlEvent(ControlEvent::Unmount));
|
||||
}
|
||||
|
||||
pub fn view_distance(&self) -> Option<u32> {
|
||||
self.view_distance
|
||||
}
|
||||
pub fn view_distance(&self) -> Option<u32> { self.view_distance }
|
||||
|
||||
pub fn loaded_distance(&self) -> f32 {
|
||||
self.loaded_distance
|
||||
}
|
||||
pub fn loaded_distance(&self) -> f32 { self.loaded_distance }
|
||||
|
||||
pub fn current_chunk(&self) -> Option<Arc<TerrainChunk>> {
|
||||
let chunk_pos = Vec2::from(
|
||||
@ -282,9 +282,7 @@ impl Client {
|
||||
self.state.terrain().get_key_arc(chunk_pos).cloned()
|
||||
}
|
||||
|
||||
pub fn inventories(&self) -> ReadStorage<comp::Inventory> {
|
||||
self.state.read_storage()
|
||||
}
|
||||
pub fn inventories(&self) -> ReadStorage<comp::Inventory> { self.state.read_storage() }
|
||||
|
||||
/// Send a chat message to the server.
|
||||
pub fn send_chat(&mut self, message: String) {
|
||||
@ -318,37 +316,40 @@ impl Client {
|
||||
)));
|
||||
}
|
||||
|
||||
/// Execute a single client tick, handle input and update the game state by the given duration.
|
||||
/// Execute a single client tick, handle input and update the game state by
|
||||
/// the given duration.
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
inputs: ControllerInputs,
|
||||
dt: Duration,
|
||||
add_foreign_systems: impl Fn(&mut DispatcherBuilder),
|
||||
) -> Result<Vec<Event>, Error> {
|
||||
// This tick function is the centre of the Veloren universe. Most client-side things are
|
||||
// managed from here, and as such it's important that it stays organised. Please consult
|
||||
// the core developers before making significant changes to this code. Here is the
|
||||
// approximate order of things. Please update it as this code changes.
|
||||
// This tick function is the centre of the Veloren universe. Most client-side
|
||||
// things are managed from here, and as such it's important that it
|
||||
// stays organised. Please consult the core developers before making
|
||||
// significant changes to this code. Here is the approximate order of
|
||||
// things. Please update it as this code changes.
|
||||
//
|
||||
// 1) Collect input from the frontend, apply input effects to the state of the game
|
||||
// 1) Collect input from the frontend, apply input effects to the state
|
||||
// of the game
|
||||
// 2) Handle messages from the server
|
||||
// 3) Go through any events (timer-driven or otherwise) that need handling and apply them
|
||||
// to the state of the game
|
||||
// 4) Perform a single LocalState tick (i.e: update the world and entities in the world)
|
||||
// 5) Go through the terrain update queue and apply all changes to the terrain
|
||||
// 3) Go through any events (timer-driven or otherwise) that need handling
|
||||
// and apply them to the state of the game
|
||||
// 4) Perform a single LocalState tick (i.e: update the world and entities
|
||||
// in the world)
|
||||
// 5) Go through the terrain update queue and apply all changes
|
||||
// to the terrain
|
||||
// 6) Sync information to the server
|
||||
// 7) Finish the tick, passing actions of the main thread back to the frontend
|
||||
// 7) Finish the tick, passing actions of the main thread back
|
||||
// to the frontend
|
||||
|
||||
// 1) Handle input from frontend.
|
||||
// Pass character actions from frontend input to the player's entity.
|
||||
if let ClientState::Character = self.client_state {
|
||||
self.state.write_component(
|
||||
self.entity,
|
||||
Controller {
|
||||
inputs: inputs.clone(),
|
||||
events: Vec::new(),
|
||||
},
|
||||
);
|
||||
self.state.write_component(self.entity, Controller {
|
||||
inputs: inputs.clone(),
|
||||
events: Vec::new(),
|
||||
});
|
||||
self.postbox
|
||||
.send_message(ClientMsg::ControllerInputs(inputs));
|
||||
}
|
||||
@ -422,7 +423,8 @@ impl Client {
|
||||
// -0.5 for being able to move to the corner of the current chunk
|
||||
// -1 because chunks are not meshed if they don't have all their neighbors
|
||||
// (notice also that view_distance is decreased by 1)
|
||||
// (this subtraction on vd is ommitted elsewhere in order to provide a buffer layer of loaded chunks)
|
||||
// (this subtraction on vd is ommitted elsewhere in order to provide
|
||||
// a buffer layer of loaded chunks)
|
||||
let top = if 2 * (dist - 2).max(0).pow(2) > (view_distance - 1).pow(2) as i32 {
|
||||
((view_distance - 1).pow(2) as f32 - (dist - 2).pow(2) as f32)
|
||||
.sqrt()
|
||||
@ -522,7 +524,8 @@ impl Client {
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
// Check that we have an valid connection.
|
||||
// Use the last ping time as a 1s rate limiter, we only notify the user once per second
|
||||
// Use the last ping time as a 1s rate limiter, we only notify the user once per
|
||||
// second
|
||||
if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) {
|
||||
let duration_since_last_pong = Instant::now().duration_since(self.last_server_pong);
|
||||
|
||||
@ -552,24 +555,37 @@ impl Client {
|
||||
ServerMsg::InitialSync { .. } => return Err(Error::ServerWentMad),
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Init(list)) => {
|
||||
self.player_list = list
|
||||
}
|
||||
},
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Add(uid, name)) => {
|
||||
if let Some(old_name) = self.player_list.insert(uid, name.clone()) {
|
||||
warn!("Received msg to insert {} with uid {} into the player list but there was already an entry for {} with the same uid that was overwritten!", name, uid, old_name);
|
||||
warn!(
|
||||
"Received msg to insert {} with uid {} into the player list but \
|
||||
there was already an entry for {} with the same uid that was \
|
||||
overwritten!",
|
||||
name, uid, old_name
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(uid)) => {
|
||||
if self.player_list.remove(&uid).is_none() {
|
||||
warn!("Received msg to remove uid {} from the player list by they weren't in the list!", uid);
|
||||
warn!(
|
||||
"Received msg to remove uid {} from the player list by they \
|
||||
weren't in the list!",
|
||||
uid
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias(uid, new_name)) => {
|
||||
if let Some(name) = self.player_list.get_mut(&uid) {
|
||||
*name = new_name;
|
||||
} else {
|
||||
warn!("Received msg to alias player with uid {} to {} but this uid is not in the player list", uid, new_name);
|
||||
warn!(
|
||||
"Received msg to alias player with uid {} to {} but this uid is \
|
||||
not in the player list",
|
||||
uid, new_name
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
|
||||
ServerMsg::Pong => {
|
||||
@ -578,26 +594,26 @@ impl Client {
|
||||
self.last_ping_delta = Instant::now()
|
||||
.duration_since(self.last_server_ping)
|
||||
.as_secs_f64();
|
||||
}
|
||||
},
|
||||
ServerMsg::ChatMsg { message, chat_type } => {
|
||||
frontend_events.push(Event::Chat { message, chat_type })
|
||||
}
|
||||
},
|
||||
ServerMsg::SetPlayerEntity(uid) => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(uid) {
|
||||
self.entity = entity;
|
||||
} else {
|
||||
return Err(Error::Other("Failed to find entity from uid.".to_owned()));
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::TimeOfDay(time_of_day) => {
|
||||
*self.state.ecs_mut().write_resource() = time_of_day;
|
||||
}
|
||||
},
|
||||
ServerMsg::EcsSync(sync_package) => {
|
||||
self.state.ecs_mut().apply_sync_package(sync_package);
|
||||
}
|
||||
},
|
||||
ServerMsg::CreateEntity(entity_package) => {
|
||||
self.state.ecs_mut().apply_entity_package(entity_package);
|
||||
}
|
||||
},
|
||||
ServerMsg::DeleteEntity(entity) => {
|
||||
if self
|
||||
.state
|
||||
@ -609,7 +625,7 @@ impl Client {
|
||||
.ecs_mut()
|
||||
.delete_entity_and_clear_from_uid_allocator(entity);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Cleanup for when the client goes back to the `Registered` state
|
||||
ServerMsg::ExitIngameCleanup => {
|
||||
// Get client entity Uid
|
||||
@ -629,22 +645,22 @@ impl Client {
|
||||
.write_resource::<UidAllocator>()
|
||||
.allocate(entity_builder.entity, Some(client_uid));
|
||||
self.entity = entity_builder.with(uid).build();
|
||||
}
|
||||
},
|
||||
ServerMsg::EntityPos { entity, pos } => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||
self.state.write_component(entity, pos);
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::EntityVel { entity, vel } => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||
self.state.write_component(entity, vel);
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::EntityOri { entity, ori } => {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||
self.state.write_component(entity, ori);
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::EntityCharacterState {
|
||||
entity,
|
||||
character_state,
|
||||
@ -652,24 +668,24 @@ impl Client {
|
||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||
self.state.write_component(entity, character_state);
|
||||
}
|
||||
}
|
||||
},
|
||||
ServerMsg::InventoryUpdate(inventory) => {
|
||||
self.state.write_component(self.entity, inventory)
|
||||
}
|
||||
},
|
||||
ServerMsg::TerrainChunkUpdate { key, chunk } => {
|
||||
if let Ok(chunk) = chunk {
|
||||
self.state.insert_chunk(key, *chunk);
|
||||
}
|
||||
self.pending_chunks.remove(&key);
|
||||
}
|
||||
},
|
||||
ServerMsg::TerrainBlockUpdates(mut blocks) => {
|
||||
blocks.drain().for_each(|(pos, block)| {
|
||||
self.state.set_block(pos, block);
|
||||
});
|
||||
}
|
||||
},
|
||||
ServerMsg::StateAnswer(Ok(state)) => {
|
||||
self.client_state = state;
|
||||
}
|
||||
},
|
||||
ServerMsg::StateAnswer(Err((error, state))) => {
|
||||
if error == RequestStateError::Denied {
|
||||
warn!("Connection denied!");
|
||||
@ -679,10 +695,10 @@ impl Client {
|
||||
"StateAnswer: {:?}. Server thinks client is in state {:?}.",
|
||||
error, state
|
||||
);
|
||||
}
|
||||
},
|
||||
ServerMsg::Disconnect => {
|
||||
frontend_events.push(Event::Disconnect);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if let Some(err) = self.postbox.error() {
|
||||
@ -695,40 +711,27 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Get the player's entity.
|
||||
pub fn entity(&self) -> EcsEntity {
|
||||
self.entity
|
||||
}
|
||||
pub fn entity(&self) -> EcsEntity { self.entity }
|
||||
|
||||
/// Get the client state
|
||||
pub fn get_client_state(&self) -> ClientState {
|
||||
self.client_state
|
||||
}
|
||||
pub fn get_client_state(&self) -> ClientState { self.client_state }
|
||||
|
||||
/// Get the current tick number.
|
||||
pub fn get_tick(&self) -> u64 {
|
||||
self.tick
|
||||
}
|
||||
pub fn get_tick(&self) -> u64 { self.tick }
|
||||
|
||||
pub fn get_ping_ms(&self) -> f64 {
|
||||
self.last_ping_delta * 1000.0
|
||||
}
|
||||
pub fn get_ping_ms(&self) -> f64 { self.last_ping_delta * 1000.0 }
|
||||
|
||||
/// Get a reference to the client's worker thread pool. This pool should be used for any
|
||||
/// computationally expensive operations that run outside of the main thread (i.e., threads that
|
||||
/// block on I/O operations are exempt).
|
||||
pub fn thread_pool(&self) -> &ThreadPool {
|
||||
&self.thread_pool
|
||||
}
|
||||
/// Get a reference to the client's worker thread pool. This pool should be
|
||||
/// used for any computationally expensive operations that run outside
|
||||
/// of the main thread (i.e., threads that block on I/O operations are
|
||||
/// exempt).
|
||||
pub fn thread_pool(&self) -> &ThreadPool { &self.thread_pool }
|
||||
|
||||
/// Get a reference to the client's game state.
|
||||
pub fn state(&self) -> &State {
|
||||
&self.state
|
||||
}
|
||||
pub fn state(&self) -> &State { &self.state }
|
||||
|
||||
/// Get a mutable reference to the client's game state.
|
||||
pub fn state_mut(&mut self) -> &mut State {
|
||||
&mut self.state
|
||||
}
|
||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||
|
||||
/// Get a vector of all the players on the server
|
||||
pub fn get_players(&mut self) -> Vec<comp::Player> {
|
||||
@ -743,7 +746,5 @@ impl Client {
|
||||
}
|
||||
|
||||
impl Drop for Client {
|
||||
fn drop(&mut self) {
|
||||
self.postbox.send_message(ClientMsg::Disconnect);
|
||||
}
|
||||
fn drop(&mut self) { self.postbox.send_message(ClientMsg::Disconnect); }
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use vek::*;
|
||||
use veloren_common::{
|
||||
|
@ -1,7 +1,4 @@
|
||||
use criterion::black_box;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use vek::*;
|
||||
use veloren_common::util::{linear_to_srgb, srgb_to_linear};
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// Get the current githash
|
||||
@ -30,7 +30,7 @@ fn main() {
|
||||
target
|
||||
.write_all(hash.trim().as_bytes())
|
||||
.expect("failed to write to file!");
|
||||
}
|
||||
},
|
||||
Err(e) => panic!("failed to convert git output to UTF-8: {}", e),
|
||||
},
|
||||
Err(e) => panic!("failed to retrieve current git commit hash: {}", e),
|
||||
|
@ -21,7 +21,8 @@ use std::{
|
||||
pub enum Error {
|
||||
/// An internal error occurred.
|
||||
Internal(Arc<dyn std::error::Error>),
|
||||
/// An asset of a different type has already been loaded with this specifier.
|
||||
/// An asset of a different type has already been loaded with this
|
||||
/// specifier.
|
||||
InvalidType,
|
||||
/// Asset does not exist.
|
||||
NotFound(String),
|
||||
@ -41,15 +42,11 @@ impl fmt::Display for Error {
|
||||
}
|
||||
|
||||
impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
||||
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self {
|
||||
Error::InvalidType
|
||||
}
|
||||
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self { Error::InvalidType }
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Error::NotFound(format!("{}", err))
|
||||
}
|
||||
fn from(err: std::io::Error) -> Self { Error::NotFound(format!("{}", err)) }
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -58,19 +55,20 @@ lazy_static! {
|
||||
RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
// TODO: Remove this function. It's only used in world/ in a really ugly way.To do this properly
|
||||
// assets should have all their necessary data in one file. A ron file could be used to combine
|
||||
// voxel data with positioning data for example.
|
||||
/// Function used to load assets from the filesystem or the cache. Permits manipulating the loaded asset with a mapping function.
|
||||
/// Example usage:
|
||||
// TODO: Remove this function. It's only used in world/ in a really ugly way.To
|
||||
// do this properly assets should have all their necessary data in one file. A
|
||||
// ron file could be used to combine voxel data with positioning data for
|
||||
// example.
|
||||
/// Function used to load assets from the filesystem or the cache. Permits
|
||||
/// manipulating the loaded asset with a mapping function. Example usage:
|
||||
/// ```no_run
|
||||
/// use veloren_common::{assets, terrain::Structure};
|
||||
/// use vek::*;
|
||||
/// use veloren_common::{assets, terrain::Structure};
|
||||
///
|
||||
/// let my_tree_structure = assets::load_map(
|
||||
/// "world.tree.oak_green.1",
|
||||
/// |s: Structure| s.with_center(Vec3::new(15, 18, 14)),
|
||||
/// ).unwrap();
|
||||
/// let my_tree_structure = assets::load_map("world.tree.oak_green.1", |s: Structure| {
|
||||
/// s.with_center(Vec3::new(15, 18, 14))
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn load_map<A: Asset + 'static, F: FnOnce(A) -> A>(
|
||||
specifier: &str,
|
||||
@ -84,7 +82,7 @@ pub fn load_map<A: Asset + 'static, F: FnOnce(A) -> A>(
|
||||
let clone = Arc::clone(&asset);
|
||||
assets_write.insert(specifier.to_owned(), clone);
|
||||
Ok(asset)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +118,7 @@ pub fn load_glob<A: Asset + 'static>(specifier: &str) -> Result<Arc<Vec<Arc<A>>>
|
||||
let mut assets_write = ASSETS.write().unwrap();
|
||||
assets_write.insert(specifier.to_owned(), clone);
|
||||
Ok(assets)
|
||||
}
|
||||
},
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
@ -137,13 +135,14 @@ pub fn load<A: Asset + 'static>(specifier: &str) -> Result<Arc<A>, Error> {
|
||||
load_map(specifier, |x| x)
|
||||
}
|
||||
|
||||
/// Function used to load assets from the filesystem or the cache and return a clone.
|
||||
/// Function used to load assets from the filesystem or the cache and return a
|
||||
/// clone.
|
||||
pub fn load_cloned<A: Asset + Clone + 'static>(specifier: &str) -> Result<A, Error> {
|
||||
load::<A>(specifier).map(|asset| (*asset).clone())
|
||||
}
|
||||
|
||||
/// Function used to load essential assets from the filesystem or the cache. It will panic if the asset is not found.
|
||||
/// Example usage:
|
||||
/// Function used to load essential assets from the filesystem or the cache. It
|
||||
/// will panic if the asset is not found. Example usage:
|
||||
/// ```no_run
|
||||
/// use image::DynamicImage;
|
||||
/// use veloren_common::assets;
|
||||
@ -159,12 +158,14 @@ pub fn load_expect<A: Asset + 'static>(specifier: &str) -> Arc<A> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Function used to load essential assets from the filesystem or the cache and return a clone. It will panic if the asset is not found.
|
||||
/// Function used to load essential assets from the filesystem or the cache and
|
||||
/// return a clone. It will panic if the asset is not found.
|
||||
pub fn load_expect_cloned<A: Asset + Clone + 'static>(specifier: &str) -> A {
|
||||
load_expect::<A>(specifier).as_ref().clone()
|
||||
}
|
||||
|
||||
/// Load an asset while registering it to be watched and reloaded when it changes
|
||||
/// Load an asset while registering it to be watched and reloaded when it
|
||||
/// changes
|
||||
pub fn load_watched<A: Asset + 'static>(
|
||||
specifier: &str,
|
||||
indicator: &mut watch::ReloadIndicator,
|
||||
@ -205,14 +206,14 @@ fn reload<A: Asset + 'static>(specifier: &str) -> Result<(), Error> {
|
||||
Some(a) => *a = clone,
|
||||
None => {
|
||||
assets_write.insert(specifier.to_owned(), clone);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The Asset trait, which is implemented by all structures that have their data stored in the
|
||||
/// filesystem.
|
||||
/// The Asset trait, which is implemented by all structures that have their data
|
||||
/// stored in the filesystem.
|
||||
pub trait Asset: Send + Sync + Sized {
|
||||
const ENDINGS: &'static [&'static str];
|
||||
/// Parse the input file and return the correct Asset.
|
||||
@ -221,6 +222,7 @@ pub trait Asset: Send + Sync + Sized {
|
||||
|
||||
impl Asset for DynamicImage {
|
||||
const ENDINGS: &'static [&'static str] = &["png", "jpg"];
|
||||
|
||||
fn parse(mut buf_reader: BufReader<File>) -> Result<Self, Error> {
|
||||
let mut buf = Vec::new();
|
||||
buf_reader.read_to_end(&mut buf)?;
|
||||
@ -230,6 +232,7 @@ impl Asset for DynamicImage {
|
||||
|
||||
impl Asset for DotVoxData {
|
||||
const ENDINGS: &'static [&'static str] = &["vox"];
|
||||
|
||||
fn parse(mut buf_reader: BufReader<File>) -> Result<Self, Error> {
|
||||
let mut buf = Vec::new();
|
||||
buf_reader.read_to_end(&mut buf)?;
|
||||
@ -240,6 +243,7 @@ impl Asset for DotVoxData {
|
||||
// Read a JSON file
|
||||
impl Asset for Value {
|
||||
const ENDINGS: &'static [&'static str] = &["json"];
|
||||
|
||||
fn parse(buf_reader: BufReader<File>) -> Result<Self, Error> {
|
||||
Ok(serde_json::from_reader(buf_reader).unwrap())
|
||||
}
|
||||
@ -247,6 +251,7 @@ impl Asset for Value {
|
||||
|
||||
impl Asset for String {
|
||||
const ENDINGS: &'static [&'static str] = &["glsl"];
|
||||
|
||||
fn parse(mut buf_reader: BufReader<File>) -> Result<Self, Error> {
|
||||
let mut string = String::new();
|
||||
buf_reader.read_to_string(&mut string)?;
|
||||
@ -300,7 +305,8 @@ lazy_static! {
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts a specifier like "core.backgrounds.city" to ".../veloren/assets/core/backgrounds/city".
|
||||
/// Converts a specifier like "core.backgrounds.city" to
|
||||
/// ".../veloren/assets/core/backgrounds/city".
|
||||
fn unpack_specifier(specifier: &str) -> PathBuf {
|
||||
let mut path = ASSETS_PATH.clone();
|
||||
path.push(specifier.replace(".", "/"));
|
||||
|
@ -19,7 +19,8 @@ lazy_static! {
|
||||
Mutex::new(Watcher::new().run());
|
||||
}
|
||||
|
||||
// This will need to be adjusted when specifier mapping to asset location becomes more dynamic
|
||||
// This will need to be adjusted when specifier mapping to asset location
|
||||
// becomes more dynamic
|
||||
struct Watcher {
|
||||
watching: HashMap<PathBuf, (Handler, Vec<Weak<AtomicBool>>)>,
|
||||
watcher: RecommendedWatcher,
|
||||
@ -37,6 +38,7 @@ impl Watcher {
|
||||
event_rx,
|
||||
}
|
||||
}
|
||||
|
||||
fn watch(&mut self, path: PathBuf, handler: Handler, signal: Weak<AtomicBool>) {
|
||||
match self.watching.get_mut(&path) {
|
||||
Some((_, ref mut v)) => {
|
||||
@ -46,16 +48,17 @@ impl Watcher {
|
||||
}) {
|
||||
v.push(signal);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if let Err(err) = self.watcher.watch(path.clone(), RecursiveMode::Recursive) {
|
||||
warn!("Could not start watching {:#?} due to: {}", &path, err);
|
||||
return;
|
||||
}
|
||||
self.watching.insert(path, (handler, vec![signal]));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: Event) {
|
||||
if let Event {
|
||||
kind: EventKind::Modify(_),
|
||||
@ -74,7 +77,7 @@ impl Watcher {
|
||||
Some(signal) => {
|
||||
signal.store(true, Ordering::Release);
|
||||
true
|
||||
}
|
||||
},
|
||||
None => false,
|
||||
});
|
||||
}
|
||||
@ -85,17 +88,22 @@ impl Watcher {
|
||||
}
|
||||
self.watching.remove(&path);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
warn!("Watching {:#?} but there are no signals for this path. The path will be unwatched.", path);
|
||||
warn!(
|
||||
"Watching {:#?} but there are no signals for this path. The path will \
|
||||
be unwatched.",
|
||||
path
|
||||
);
|
||||
if let Err(err) = self.watcher.unwatch(&path) {
|
||||
warn!("Error unwatching: {}", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(mut self) -> Sender<(PathBuf, Handler, Weak<AtomicBool>)> {
|
||||
let (watch_tx, watch_rx) = unbounded();
|
||||
|
||||
@ -134,6 +142,7 @@ impl ReloadIndicator {
|
||||
paths: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add<F>(&mut self, path: PathBuf, reloader: F)
|
||||
where
|
||||
F: 'static + Fn() + Send,
|
||||
@ -155,8 +164,7 @@ impl ReloadIndicator {
|
||||
error!("Could not add. Asset watcher channel disconnected.");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the watched file was changed
|
||||
pub fn reloaded(&self) -> bool {
|
||||
self.reloaded.swap(false, Ordering::Acquire)
|
||||
}
|
||||
pub fn reloaded(&self) -> bool { self.reloaded.swap(false, Ordering::Acquire) }
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
use crate::path::Path;
|
||||
use core::cmp::Ordering::Equal;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BinaryHeap;
|
||||
use std::f32;
|
||||
use std::hash::Hash;
|
||||
use std::{cmp::Ordering, collections::BinaryHeap, f32, hash::Hash};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct PathEntry<S> {
|
||||
@ -13,9 +10,7 @@ pub struct PathEntry<S> {
|
||||
}
|
||||
|
||||
impl<S: Eq> PartialEq for PathEntry<S> {
|
||||
fn eq(&self, other: &PathEntry<S>) -> bool {
|
||||
self.node.eq(&other.node)
|
||||
}
|
||||
fn eq(&self, other: &PathEntry<S>) -> bool { self.node.eq(&other.node) }
|
||||
}
|
||||
|
||||
impl<S: Eq> Eq for PathEntry<S> {}
|
||||
@ -29,9 +24,7 @@ impl<S: Eq> Ord for PathEntry<S> {
|
||||
}
|
||||
|
||||
impl<S: Eq> PartialOrd for PathEntry<S> {
|
||||
fn partial_cmp(&self, other: &PathEntry<S>) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
fn partial_cmp(&self, other: &PathEntry<S>) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
pub enum PathResult<T> {
|
||||
|
@ -22,17 +22,13 @@ impl Clock {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tps(&self) -> f64 {
|
||||
1.0 / self.running_tps_average
|
||||
}
|
||||
pub fn get_tps(&self) -> f64 { 1.0 / self.running_tps_average }
|
||||
|
||||
pub fn get_last_delta(&self) -> Duration {
|
||||
self.last_delta.unwrap_or_else(|| Duration::new(0, 0))
|
||||
}
|
||||
|
||||
pub fn get_avg_delta(&self) -> Duration {
|
||||
Duration::from_secs_f64(self.running_tps_average)
|
||||
}
|
||||
pub fn get_avg_delta(&self) -> Duration { Duration::from_secs_f64(self.running_tps_average) }
|
||||
|
||||
pub fn tick(&mut self, tgt: Duration) {
|
||||
let delta = Instant::now().duration_since(self.last_sys_time);
|
||||
|
@ -81,7 +81,5 @@ impl Activity {
|
||||
}
|
||||
|
||||
impl Default for Activity {
|
||||
fn default() -> Self {
|
||||
Activity::Idle(Vec2::zero())
|
||||
}
|
||||
fn default() -> Self { Activity::Idle(Vec2::zero()) }
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ pub struct BodyData<BodyMeta, SpeciesData> {
|
||||
pub species: SpeciesData,
|
||||
}
|
||||
|
||||
/// Metadata intended to be stored per-body, together with data intended to be stored for each
|
||||
/// species for each body.
|
||||
/// Metadata intended to be stored per-body, together with data intended to be
|
||||
/// stored for each species for each body.
|
||||
///
|
||||
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
@ -76,11 +76,12 @@ impl<BodyMeta, SpeciesMeta> core::ops::Index<NpcKind> for AllBodies<BodyMeta, Sp
|
||||
}
|
||||
|
||||
impl<
|
||||
BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
||||
SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
||||
> Asset for AllBodies<BodyMeta, SpeciesMeta>
|
||||
BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
||||
SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
||||
> Asset for AllBodies<BodyMeta, SpeciesMeta>
|
||||
{
|
||||
const ENDINGS: &'static [&'static str] = &["json"];
|
||||
|
||||
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
|
||||
serde_json::de::from_reader(buf_reader).map_err(|e| assets::Error::Internal(Arc::new(e)))
|
||||
}
|
||||
@ -93,6 +94,7 @@ impl Body {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this might need to be refined to something more complex for realistic
|
||||
// behavior with less cylindrical bodies (e.g. wolfs)
|
||||
pub fn radius(&self) -> f32 {
|
||||
|
@ -310,6 +310,7 @@ impl Race {
|
||||
Race::Undead => &UNDEAD_HAIR_COLORS,
|
||||
}
|
||||
}
|
||||
|
||||
fn skin_colors(self) -> &'static [Skin] {
|
||||
match self {
|
||||
Race::Danari => &DANARI_SKIN_COLORS,
|
||||
@ -320,6 +321,7 @@ impl Race {
|
||||
Race::Undead => &UNDEAD_SKIN_COLORS,
|
||||
}
|
||||
}
|
||||
|
||||
fn eye_colors(self) -> &'static [EyeColor] {
|
||||
match self {
|
||||
Race::Danari => &DANARI_EYE_COLORS,
|
||||
@ -330,6 +332,7 @@ impl Race {
|
||||
Race::Undead => &UNDEAD_EYE_COLORS,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hair_color(self, val: u8) -> Rgb<u8> {
|
||||
self.hair_colors()
|
||||
.get(val as usize)
|
||||
@ -337,27 +340,27 @@ impl Race {
|
||||
.unwrap_or((0, 0, 0))
|
||||
.into()
|
||||
}
|
||||
pub fn num_hair_colors(self) -> u8 {
|
||||
self.hair_colors().len() as u8
|
||||
}
|
||||
|
||||
pub fn num_hair_colors(self) -> u8 { self.hair_colors().len() as u8 }
|
||||
|
||||
pub fn skin_color(self, val: u8) -> Skin {
|
||||
self.skin_colors()
|
||||
.get(val as usize)
|
||||
.copied()
|
||||
.unwrap_or(Skin::Tanned)
|
||||
}
|
||||
pub fn num_skin_colors(self) -> u8 {
|
||||
self.skin_colors().len() as u8
|
||||
}
|
||||
|
||||
pub fn num_skin_colors(self) -> u8 { self.skin_colors().len() as u8 }
|
||||
|
||||
pub fn eye_color(self, val: u8) -> EyeColor {
|
||||
self.eye_colors()
|
||||
.get(val as usize)
|
||||
.copied()
|
||||
.unwrap_or(EyeColor::NobleBlue)
|
||||
}
|
||||
pub fn num_eye_colors(self) -> u8 {
|
||||
self.eye_colors().len() as u8
|
||||
}
|
||||
|
||||
pub fn num_eye_colors(self) -> u8 { self.eye_colors().len() as u8 }
|
||||
|
||||
pub fn num_hair_styles(self, body_type: BodyType) -> u8 {
|
||||
match (self, body_type) {
|
||||
(Race::Danari, BodyType::Female) => 2,
|
||||
@ -374,6 +377,7 @@ impl Race {
|
||||
(Race::Undead, BodyType::Male) => 3,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_accessories(self, body_type: BodyType) -> u8 {
|
||||
match (self, body_type) {
|
||||
(Race::Danari, BodyType::Female) => 1,
|
||||
@ -390,6 +394,7 @@ impl Race {
|
||||
(Race::Undead, BodyType::Male) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_beards(self, body_type: BodyType) -> u8 {
|
||||
match (self, body_type) {
|
||||
(Race::Danari, BodyType::Female) => 1,
|
||||
@ -527,6 +532,7 @@ impl EyeColor {
|
||||
EyeColor::ExoticPurple => Rgb::new(95, 32, 111),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dark_rgb(self) -> Rgb<u8> {
|
||||
match self {
|
||||
EyeColor::VigorousBlack => Rgb::new(32, 32, 32),
|
||||
@ -541,9 +547,8 @@ impl EyeColor {
|
||||
EyeColor::ExoticPurple => Rgb::new(69, 23, 80),
|
||||
}
|
||||
}
|
||||
pub fn white_rgb(self) -> Rgb<u8> {
|
||||
Rgb::new(255, 255, 255)
|
||||
}
|
||||
|
||||
pub fn white_rgb(self) -> Rgb<u8> { Rgb::new(255, 255, 255) }
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
@ -606,6 +611,7 @@ impl Skin {
|
||||
};
|
||||
Rgb::from(color)
|
||||
}
|
||||
|
||||
pub fn light_rgb(self) -> Rgb<u8> {
|
||||
let color = match self {
|
||||
Self::Pale => (255, 227, 193),
|
||||
@ -632,6 +638,7 @@ impl Skin {
|
||||
};
|
||||
Rgb::from(color)
|
||||
}
|
||||
|
||||
pub fn dark_rgb(self) -> Rgb<u8> {
|
||||
let color = match self {
|
||||
Self::Pale => (229, 192, 163),
|
||||
|
@ -101,10 +101,12 @@ impl CharacterState {
|
||||
// Check if enum item is the same without looking at the inner data
|
||||
std::mem::discriminant(&self.movement) == std::mem::discriminant(&other.movement)
|
||||
}
|
||||
|
||||
pub fn is_same_action(&self, other: &Self) -> bool {
|
||||
// Check if enum item is the same without looking at the inner data
|
||||
std::mem::discriminant(&self.action) == std::mem::discriminant(&other.action)
|
||||
}
|
||||
|
||||
pub fn is_same_state(&self, other: &Self) -> bool {
|
||||
self.is_same_movement(other) && self.is_same_action(other)
|
||||
}
|
||||
|
@ -53,15 +53,11 @@ impl Input {
|
||||
}
|
||||
|
||||
/// Whether input is in `InputState::Pressed` state
|
||||
pub fn is_pressed(&self) -> bool {
|
||||
self.state == InputState::Pressed
|
||||
}
|
||||
pub fn is_pressed(&self) -> bool { self.state == InputState::Pressed }
|
||||
|
||||
/// Whether it's the first frame this input has been in
|
||||
/// its current state
|
||||
pub fn is_just_pressed(&self) -> bool {
|
||||
(self.just_changed && self.is_pressed())
|
||||
}
|
||||
pub fn is_just_pressed(&self) -> bool { (self.just_changed && self.is_pressed()) }
|
||||
|
||||
/// Whether input has been in current state longer than
|
||||
/// `DEFAULT_HOLD_DURATION`
|
||||
@ -78,14 +74,14 @@ impl Input {
|
||||
self.dirty = true;
|
||||
self.state = InputState::Pressed;
|
||||
self.duration = Duration::default();
|
||||
}
|
||||
},
|
||||
(true, false) => {
|
||||
self.just_changed = true;
|
||||
self.dirty = true;
|
||||
self.state = InputState::Unpressed;
|
||||
self.duration = Duration::default();
|
||||
}
|
||||
(_, _) => {}
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -95,9 +91,7 @@ impl Input {
|
||||
}
|
||||
|
||||
/// Returns `input::duration`
|
||||
pub fn get_dur(&self) -> Duration {
|
||||
self.duration
|
||||
}
|
||||
pub fn get_dur(&self) -> Duration { self.duration }
|
||||
}
|
||||
|
||||
impl Default for Input {
|
||||
@ -154,6 +148,7 @@ impl ControllerInputs {
|
||||
self.toggle_wield.tick(dt);
|
||||
self.charge.tick(dt);
|
||||
}
|
||||
|
||||
/// Updates `inputs.move_dir`.
|
||||
pub fn update_move_dir(&mut self) {
|
||||
self.move_dir = if self.move_dir.magnitude_squared() > 1.0 {
|
||||
@ -163,6 +158,7 @@ impl ControllerInputs {
|
||||
self.move_dir
|
||||
};
|
||||
}
|
||||
|
||||
/// Updates `inputs.look_dir`
|
||||
pub fn update_look_dir(&mut self) {
|
||||
self.look_dir
|
||||
@ -173,17 +169,11 @@ impl ControllerInputs {
|
||||
|
||||
impl Controller {
|
||||
/// Sets all inputs to default
|
||||
pub fn reset(&mut self) {
|
||||
*self = Self::default();
|
||||
}
|
||||
pub fn reset(&mut self) { *self = Self::default(); }
|
||||
|
||||
pub fn clear_events(&mut self) {
|
||||
self.events.clear();
|
||||
}
|
||||
pub fn clear_events(&mut self) { self.events.clear(); }
|
||||
|
||||
pub fn push_event(&mut self, event: ControlEvent) {
|
||||
self.events.push(event);
|
||||
}
|
||||
pub fn push_event(&mut self, event: ControlEvent) { self.events.push(event); }
|
||||
}
|
||||
|
||||
impl Component for Controller {
|
||||
|
@ -34,13 +34,9 @@ impl Energy {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current(&self) -> u32 {
|
||||
self.current
|
||||
}
|
||||
pub fn current(&self) -> u32 { self.current }
|
||||
|
||||
pub fn maximum(&self) -> u32 {
|
||||
self.maximum
|
||||
}
|
||||
pub fn maximum(&self) -> u32 { self.maximum }
|
||||
|
||||
pub fn set_to(&mut self, amount: u32, cause: EnergySource) {
|
||||
let amount = amount.min(self.maximum);
|
||||
|
@ -7,9 +7,7 @@ use crate::{
|
||||
use rand::seq::SliceRandom;
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::time::Duration;
|
||||
use std::{fs::File, io::BufReader, time::Duration};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Tool {
|
||||
@ -37,6 +35,7 @@ impl Tool {
|
||||
Tool::Debug(_) => Duration::from_millis(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attack_buildup_duration(&self) -> Duration {
|
||||
match self {
|
||||
Tool::Sword => Duration::from_millis(100),
|
||||
@ -49,6 +48,7 @@ impl Tool {
|
||||
Tool::Debug(_) => Duration::from_millis(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attack_recover_duration(&self) -> Duration {
|
||||
match self {
|
||||
Tool::Sword => Duration::from_millis(500),
|
||||
@ -61,6 +61,7 @@ impl Tool {
|
||||
Tool::Debug(_) => Duration::from_millis(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attack_duration(&self) -> Duration {
|
||||
self.attack_buildup_duration() + self.attack_recover_duration()
|
||||
}
|
||||
@ -74,7 +75,8 @@ pub enum Debug {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Armor {
|
||||
// TODO: Don't make armor be a body part. Wearing enemy's head is funny but also a creepy thing to do.
|
||||
// TODO: Don't make armor be a body part. Wearing enemy's head is funny but also a creepy
|
||||
// thing to do.
|
||||
Helmet,
|
||||
Shoulders,
|
||||
Chestplate,
|
||||
@ -128,18 +130,17 @@ pub struct Item {
|
||||
|
||||
impl Asset for Item {
|
||||
const ENDINGS: &'static [&'static str] = &["ron"];
|
||||
|
||||
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
|
||||
Ok(ron::de::from_reader(buf_reader).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn description(&self) -> &str {
|
||||
&self.description
|
||||
}
|
||||
pub fn name(&self) -> &str { &self.name }
|
||||
|
||||
pub fn description(&self) -> &str { &self.description }
|
||||
|
||||
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
|
||||
match block.kind() {
|
||||
BlockKind::Apple => Some(assets::load_expect_cloned("common.items.apple")),
|
||||
@ -149,19 +150,19 @@ impl Item {
|
||||
BlockKind::PinkFlower => Some(assets::load_expect_cloned("common.items.flowers.pink")),
|
||||
BlockKind::PurpleFlower => {
|
||||
Some(assets::load_expect_cloned("common.items.flowers.purple"))
|
||||
}
|
||||
},
|
||||
BlockKind::RedFlower => Some(assets::load_expect_cloned("common.items.flowers.red")),
|
||||
BlockKind::WhiteFlower => {
|
||||
Some(assets::load_expect_cloned("common.items.flowers.white"))
|
||||
}
|
||||
},
|
||||
BlockKind::YellowFlower => {
|
||||
Some(assets::load_expect_cloned("common.items.flowers.yellow"))
|
||||
}
|
||||
},
|
||||
BlockKind::Sunflower => Some(assets::load_expect_cloned("common.items.flowers.sun")),
|
||||
BlockKind::LongGrass => Some(assets::load_expect_cloned("common.items.grasses.long")),
|
||||
BlockKind::MediumGrass => {
|
||||
Some(assets::load_expect_cloned("common.items.grasses.medium"))
|
||||
}
|
||||
},
|
||||
BlockKind::ShortGrass => Some(assets::load_expect_cloned("common.items.grasses.short")),
|
||||
BlockKind::Chest => Some(assets::load_expect_cloned(
|
||||
[
|
||||
|
@ -15,32 +15,30 @@ pub struct Inventory {
|
||||
/// Errors which the methods on `Inventory` produce
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// The inventory is full and items could not be added. The extra items have been returned.
|
||||
/// The inventory is full and items could not be added. The extra items have
|
||||
/// been returned.
|
||||
Full(Vec<Item>),
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
pub fn slots(&self) -> &[Option<Item>] {
|
||||
&self.slots
|
||||
}
|
||||
pub fn slots(&self) -> &[Option<Item>] { &self.slots }
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.slots.len()
|
||||
}
|
||||
pub fn len(&self) -> usize { self.slots.len() }
|
||||
|
||||
/// Adds a new item to the first empty slot of the inventory. Returns the item again if no free
|
||||
/// slot was found.
|
||||
/// Adds a new item to the first empty slot of the inventory. Returns the
|
||||
/// item again if no free slot was found.
|
||||
pub fn push(&mut self, item: Item) -> Option<Item> {
|
||||
match self.slots.iter_mut().find(|slot| slot.is_none()) {
|
||||
Some(slot) => {
|
||||
*slot = Some(item);
|
||||
None
|
||||
}
|
||||
},
|
||||
None => Some(item),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a series of items to inventory, returning any which do not fit as an error.
|
||||
/// Add a series of items to inventory, returning any which do not fit as an
|
||||
/// error.
|
||||
pub fn push_all<I: Iterator<Item = Item>>(&mut self, mut items: I) -> Result<(), Error> {
|
||||
// Vec doesn't allocate for zero elements so this should be cheap
|
||||
let mut leftovers = Vec::new();
|
||||
@ -59,12 +57,15 @@ impl Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a series of items to an inventory without giving duplicates. (n * m complexity)
|
||||
/// Add a series of items to an inventory without giving duplicates.
|
||||
/// (n * m complexity)
|
||||
///
|
||||
/// Error if inventory cannot contain the items (is full), returning the un-added items.
|
||||
/// This is a lazy inefficient implementation, as it iterates over the inventory more times
|
||||
/// than necessary (n^2) and with the proper structure wouldn't need to iterate at all, but because
|
||||
/// this should be fairly cold code, clarity has been favored over efficiency.
|
||||
/// Error if inventory cannot contain the items (is full), returning the
|
||||
/// un-added items. This is a lazy inefficient implementation, as it
|
||||
/// iterates over the inventory more times than necessary (n^2) and with
|
||||
/// the proper structure wouldn't need to iterate at all, but because
|
||||
/// this should be fairly cold code, clarity has been favored over
|
||||
/// efficiency.
|
||||
pub fn push_all_unique<I: Iterator<Item = Item>>(&mut self, mut items: I) -> Result<(), Error> {
|
||||
let mut leftovers = Vec::new();
|
||||
for item in &mut items {
|
||||
@ -79,27 +80,23 @@ impl Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces an item in a specific slot of the inventory. Returns the old item or the same item again if that slot
|
||||
/// was not found.
|
||||
/// Replaces an item in a specific slot of the inventory. Returns the old
|
||||
/// item or the same item again if that slot was not found.
|
||||
pub fn insert(&mut self, cell: usize, item: Item) -> Result<Option<Item>, Item> {
|
||||
match self.slots.get_mut(cell) {
|
||||
Some(slot) => {
|
||||
let old = slot.take();
|
||||
*slot = Some(item);
|
||||
Ok(old)
|
||||
}
|
||||
},
|
||||
None => Err(item),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.slots.iter().all(|slot| slot.is_some())
|
||||
}
|
||||
pub fn is_full(&self) -> bool { self.slots.iter().all(|slot| slot.is_some()) }
|
||||
|
||||
/// O(n) count the number of items in this inventory.
|
||||
pub fn count(&self) -> usize {
|
||||
self.slots.iter().filter_map(|slot| slot.as_ref()).count()
|
||||
}
|
||||
pub fn count(&self) -> usize { self.slots.iter().filter_map(|slot| slot.as_ref()).count() }
|
||||
|
||||
/// O(n) check if an item is in this inventory.
|
||||
pub fn contains(&self, item: &Item) -> bool {
|
||||
@ -147,5 +144,4 @@ impl Component for InventoryUpdate {
|
||||
type Storage = NullStorage<Self>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
#[cfg(test)] mod test;
|
||||
|
@ -8,9 +8,7 @@ lazy_static! {
|
||||
}
|
||||
/// The `Default` inventory should contain two items
|
||||
#[test]
|
||||
fn create_default_count() {
|
||||
assert_eq!(Inventory::default().count(), 2)
|
||||
}
|
||||
fn create_default_count() { assert_eq!(Inventory::default().count(), 2) }
|
||||
|
||||
/// Attempting to push into a full inventory should return the same item.
|
||||
#[test]
|
||||
@ -36,7 +34,8 @@ fn push_all_full() {
|
||||
assert_eq!(leftovers, TEST_ITEMS.clone())
|
||||
}
|
||||
|
||||
/// Attempting to push uniquely into an inventory containing all the items should work fine.
|
||||
/// Attempting to push uniquely into an inventory containing all the items
|
||||
/// should work fine.
|
||||
#[test]
|
||||
fn push_unique_all_full() {
|
||||
let mut inv = Inventory {
|
||||
@ -46,7 +45,8 @@ fn push_unique_all_full() {
|
||||
.expect("Pushing unique items into an inventory that already contains them didn't work!");
|
||||
}
|
||||
|
||||
/// Attempting to push uniquely into an inventory containing all the items should work fine.
|
||||
/// Attempting to push uniquely into an inventory containing all the items
|
||||
/// should work fine.
|
||||
#[test]
|
||||
fn push_all_empty() {
|
||||
let mut inv = Inventory {
|
||||
@ -56,7 +56,8 @@ fn push_all_empty() {
|
||||
.expect("Pushing items into an empty inventory didn't work!");
|
||||
}
|
||||
|
||||
/// Attempting to push uniquely into an inventory containing all the items should work fine.
|
||||
/// Attempting to push uniquely into an inventory containing all the items
|
||||
/// should work fine.
|
||||
#[test]
|
||||
fn push_all_unique_empty() {
|
||||
let mut inv = Inventory {
|
||||
|
@ -8,13 +8,9 @@ pub struct Waypoint {
|
||||
}
|
||||
|
||||
impl Waypoint {
|
||||
pub fn new(pos: Vec3<f32>) -> Self {
|
||||
Self { pos }
|
||||
}
|
||||
pub fn new(pos: Vec3<f32>) -> Self { Self { pos } }
|
||||
|
||||
pub fn get_pos(&self) -> Vec3<f32> {
|
||||
self.pos
|
||||
}
|
||||
pub fn get_pos(&self) -> Vec3<f32> { self.pos }
|
||||
}
|
||||
|
||||
impl Component for Waypoint {
|
||||
@ -25,9 +21,7 @@ impl Component for Waypoint {
|
||||
pub struct WaypointArea(f32);
|
||||
|
||||
impl WaypointArea {
|
||||
pub fn radius(&self) -> f32 {
|
||||
self.0
|
||||
}
|
||||
pub fn radius(&self) -> f32 { self.0 }
|
||||
}
|
||||
|
||||
impl Component for WaypointArea {
|
||||
@ -35,7 +29,5 @@ impl Component for WaypointArea {
|
||||
}
|
||||
|
||||
impl Default for WaypointArea {
|
||||
fn default() -> Self {
|
||||
Self(5.0)
|
||||
}
|
||||
fn default() -> Self { Self(5.0) }
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::comp::{body::humanoid::Race, Body};
|
||||
use crate::{comp, sync::Uid};
|
||||
use crate::{
|
||||
comp,
|
||||
comp::{body::humanoid::Race, Body},
|
||||
sync::Uid,
|
||||
};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
|
||||
@ -47,23 +50,16 @@ pub struct Equipment {
|
||||
}
|
||||
|
||||
impl Health {
|
||||
pub fn current(&self) -> u32 {
|
||||
self.current
|
||||
}
|
||||
pub fn current(&self) -> u32 { self.current }
|
||||
|
||||
pub fn maximum(&self) -> u32 {
|
||||
self.maximum
|
||||
}
|
||||
pub fn maximum(&self) -> u32 { self.maximum }
|
||||
|
||||
pub fn set_to(&mut self, amount: u32, cause: HealthSource) {
|
||||
let amount = amount.min(self.maximum);
|
||||
self.last_change = (
|
||||
0.0,
|
||||
HealthChange {
|
||||
amount: amount as i32 - self.current as i32,
|
||||
cause,
|
||||
},
|
||||
);
|
||||
self.last_change = (0.0, HealthChange {
|
||||
amount: amount as i32 - self.current as i32,
|
||||
cause,
|
||||
});
|
||||
self.current = amount;
|
||||
}
|
||||
|
||||
@ -83,34 +79,23 @@ pub enum StatChangeError {
|
||||
Underflow,
|
||||
Overflow,
|
||||
}
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::{error::Error, fmt};
|
||||
impl fmt::Display for StatChangeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Underflow => "insufficient stat quantity",
|
||||
Self::Overflow => "stat quantity would overflow",
|
||||
}
|
||||
)
|
||||
write!(f, "{}", match self {
|
||||
Self::Underflow => "insufficient stat quantity",
|
||||
Self::Overflow => "stat quantity would overflow",
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Error for StatChangeError {}
|
||||
|
||||
impl Exp {
|
||||
pub fn current(&self) -> u32 {
|
||||
self.current
|
||||
}
|
||||
pub fn current(&self) -> u32 { self.current }
|
||||
|
||||
pub fn maximum(&self) -> u32 {
|
||||
self.maximum
|
||||
}
|
||||
pub fn maximum(&self) -> u32 { self.maximum }
|
||||
|
||||
pub fn set_current(&mut self, current: u32) {
|
||||
self.current = current;
|
||||
}
|
||||
pub fn set_current(&mut self, current: u32) { self.current = current; }
|
||||
|
||||
// TODO: Uncomment when needed
|
||||
// pub fn set_maximum(&mut self, maximum: u32) {
|
||||
@ -127,17 +112,11 @@ impl Exp {
|
||||
}
|
||||
|
||||
impl Level {
|
||||
pub fn set_level(&mut self, level: u32) {
|
||||
self.amount = level;
|
||||
}
|
||||
pub fn set_level(&mut self, level: u32) { self.amount = level; }
|
||||
|
||||
pub fn level(&self) -> u32 {
|
||||
self.amount
|
||||
}
|
||||
pub fn level(&self) -> u32 { self.amount }
|
||||
|
||||
pub fn change_by(&mut self, level: u32) {
|
||||
self.amount += level;
|
||||
}
|
||||
pub fn change_by(&mut self, level: u32) { self.amount += level; }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@ -154,9 +133,8 @@ pub struct Stats {
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
pub fn should_die(&self) -> bool {
|
||||
self.health.current == 0
|
||||
}
|
||||
pub fn should_die(&self) -> bool { self.health.current == 0 }
|
||||
|
||||
pub fn revive(&mut self) {
|
||||
self.health
|
||||
.set_to(self.health.maximum(), HealthSource::Revive);
|
||||
@ -164,9 +142,7 @@ impl Stats {
|
||||
}
|
||||
|
||||
// TODO: Delete this once stat points will be a thing
|
||||
pub fn update_max_hp(&mut self) {
|
||||
self.health.set_maximum(27 + 15 * self.level.amount);
|
||||
}
|
||||
pub fn update_max_hp(&mut self) { self.health.set_maximum(27 + 15 * self.level.amount); }
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
@ -192,13 +168,10 @@ impl Stats {
|
||||
health: Health {
|
||||
current: 0,
|
||||
maximum: 0,
|
||||
last_change: (
|
||||
0.0,
|
||||
HealthChange {
|
||||
amount: 0,
|
||||
cause: HealthSource::Revive,
|
||||
},
|
||||
),
|
||||
last_change: (0.0, HealthChange {
|
||||
amount: 0,
|
||||
cause: HealthSource::Revive,
|
||||
}),
|
||||
},
|
||||
level: Level { amount: 1 },
|
||||
exp: Exp {
|
||||
|
@ -12,13 +12,9 @@ pub struct SfxEventItem {
|
||||
}
|
||||
|
||||
impl SfxEventItem {
|
||||
pub fn new(sfx: SfxEvent, pos: Option<Vec3<f32>>) -> Self {
|
||||
Self { sfx, pos }
|
||||
}
|
||||
pub fn new(sfx: SfxEvent, pos: Option<Vec3<f32>>) -> Self { Self { sfx, pos } }
|
||||
|
||||
pub fn at_player_position(sfx: SfxEvent) -> Self {
|
||||
Self { sfx, pos: None }
|
||||
}
|
||||
pub fn at_player_position(sfx: SfxEvent) -> Self { Self { sfx, pos: None } }
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
@ -131,9 +127,7 @@ impl<E> EventBus<E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit(&self, event: E) {
|
||||
self.queue.lock().push_front(event);
|
||||
}
|
||||
pub fn emit(&self, event: E) { self.queue.lock().push_front(event); }
|
||||
|
||||
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> {
|
||||
std::mem::replace(self.queue.lock().deref_mut(), VecDeque::new()).into_iter()
|
||||
@ -146,13 +140,9 @@ pub struct Emitter<'a, E> {
|
||||
}
|
||||
|
||||
impl<'a, E> Emitter<'a, E> {
|
||||
pub fn emit(&mut self, event: E) {
|
||||
self.events.push_front(event);
|
||||
}
|
||||
pub fn emit(&mut self, event: E) { self.events.push_front(event); }
|
||||
}
|
||||
|
||||
impl<'a, E> Drop for Emitter<'a, E> {
|
||||
fn drop(&mut self) {
|
||||
self.bus.queue.lock().append(&mut self.events);
|
||||
}
|
||||
fn drop(&mut self) { self.bus.queue.lock().append(&mut self.events); }
|
||||
}
|
||||
|
@ -9,9 +9,7 @@ pub enum Cell {
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn new(rgb: Rgb<u8>) -> Self {
|
||||
Cell::Filled(rgb.into_array())
|
||||
}
|
||||
pub fn new(rgb: Rgb<u8>) -> Self { Cell::Filled(rgb.into_array()) }
|
||||
|
||||
pub fn get_color(&self) -> Option<Rgb<u8>> {
|
||||
match self {
|
||||
@ -22,9 +20,7 @@ impl Cell {
|
||||
}
|
||||
|
||||
impl Vox for Cell {
|
||||
fn empty() -> Self {
|
||||
Cell::Empty
|
||||
}
|
||||
fn empty() -> Self { Cell::Empty }
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
|
@ -10,9 +10,9 @@ pub enum Material {
|
||||
EyeDark,
|
||||
EyeLight,
|
||||
EyeWhite,
|
||||
//HairLight,
|
||||
//HairDark,
|
||||
//Clothing,
|
||||
/*HairLight,
|
||||
*HairDark,
|
||||
*Clothing, */
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
@ -23,9 +23,7 @@ pub enum MatCell {
|
||||
}
|
||||
|
||||
impl Vox for MatCell {
|
||||
fn empty() -> Self {
|
||||
MatCell::None
|
||||
}
|
||||
fn empty() -> Self { MatCell::None }
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
|
@ -2,8 +2,7 @@ pub mod cell;
|
||||
pub mod mat_cell;
|
||||
pub use mat_cell::Material;
|
||||
|
||||
use self::cell::Cell;
|
||||
use self::mat_cell::MatCell;
|
||||
use self::{cell::Cell, mat_cell::MatCell};
|
||||
use crate::{
|
||||
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol},
|
||||
volumes::dyna::Dyna,
|
||||
@ -60,6 +59,7 @@ impl Segment {
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Transform cell colors
|
||||
pub fn map_rgb(self, transform: impl Fn(Rgb<u8>) -> Rgb<u8>) -> Self {
|
||||
self.map(|cell| cell.get_color().map(|rgb| Cell::new(transform(rgb))))
|
||||
@ -71,19 +71,20 @@ impl Segment {
|
||||
pub struct DynaUnionizer<V: Vox>(Vec<(Dyna<V, ()>, Vec3<i32>)>);
|
||||
|
||||
impl<V: Vox + Copy> DynaUnionizer<V> {
|
||||
pub fn new() -> Self {
|
||||
DynaUnionizer(Vec::new())
|
||||
}
|
||||
pub fn new() -> Self { DynaUnionizer(Vec::new()) }
|
||||
|
||||
pub fn add(mut self, dyna: Dyna<V, ()>, offset: Vec3<i32>) -> Self {
|
||||
self.0.push((dyna, offset));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn maybe_add(self, maybe: Option<(Dyna<V, ()>, Vec3<i32>)>) -> Self {
|
||||
match maybe {
|
||||
Some((dyna, offset)) => self.add(dyna, offset),
|
||||
None => self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unify(self) -> (Dyna<V, ()>, Vec3<i32>) {
|
||||
if self.0.is_empty() {
|
||||
return (Dyna::filled(Vec3::zero(), V::empty(), ()), Vec3::zero());
|
||||
@ -129,6 +130,7 @@ impl MatSegment {
|
||||
}
|
||||
vol
|
||||
}
|
||||
|
||||
/// Transform cells
|
||||
pub fn map(mut self, transform: impl Fn(MatCell) -> Option<MatCell>) -> Self {
|
||||
for pos in self.full_pos_iter() {
|
||||
@ -139,6 +141,7 @@ impl MatSegment {
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Transform cell colors
|
||||
pub fn map_rgb(self, transform: impl Fn(Rgb<u8>) -> Rgb<u8>) -> Self {
|
||||
self.map(|cell| match cell {
|
||||
@ -179,7 +182,7 @@ impl From<&DotVoxData> for MatSegment {
|
||||
.copied()
|
||||
.unwrap_or_else(|| Rgb::broadcast(0));
|
||||
MatCell::Normal(color)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
vol.set(
|
||||
|
@ -2,10 +2,8 @@
|
||||
#![type_length_limit = "1664759"]
|
||||
#![feature(trait_alias, arbitrary_enum_discriminant, label_break_value)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
pub mod assets;
|
||||
pub mod astar;
|
||||
@ -28,11 +26,12 @@ pub mod util;
|
||||
pub mod vol;
|
||||
pub mod volumes;
|
||||
|
||||
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client.
|
||||
/// # Examples
|
||||
/// The networking module containing high-level wrappers of `TcpListener` and
|
||||
/// `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by
|
||||
/// both the server and client. # Examples
|
||||
/// ```
|
||||
/// use std::net::SocketAddr;
|
||||
/// use veloren_common::net::{PostOffice, PostBox};
|
||||
/// use veloren_common::net::{PostBox, PostOffice};
|
||||
///
|
||||
/// let listen_addr = SocketAddr::from(([0, 0, 0, 0], 12345u16));
|
||||
/// let conn_addr = SocketAddr::from(([127, 0, 0, 1], 12345u16));
|
||||
|
@ -45,6 +45,7 @@ sum_type! {
|
||||
}
|
||||
impl sync::CompPacket for EcsCompPacket {
|
||||
type Phantom = EcsCompPhantom;
|
||||
|
||||
fn apply_insert(self, entity: specs::Entity, world: &specs::World) {
|
||||
match self {
|
||||
EcsCompPacket::Body(comp) => sync::handle_insert(comp, entity, world),
|
||||
@ -62,6 +63,7 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_modify(self, entity: specs::Entity, world: &specs::World) {
|
||||
match self {
|
||||
EcsCompPacket::Body(comp) => sync::handle_modify(comp, entity, world),
|
||||
@ -79,6 +81,7 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_remove(phantom: Self::Phantom, entity: specs::Entity, world: &specs::World) {
|
||||
match phantom {
|
||||
EcsCompPhantom::Body(_) => sync::handle_remove::<comp::Body>(entity, world),
|
||||
@ -88,7 +91,7 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPhantom::Energy(_) => sync::handle_remove::<comp::Energy>(entity, world),
|
||||
EcsCompPhantom::LightEmitter(_) => {
|
||||
sync::handle_remove::<comp::LightEmitter>(entity, world)
|
||||
}
|
||||
},
|
||||
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
|
||||
EcsCompPhantom::Scale(_) => sync::handle_remove::<comp::Scale>(entity, world),
|
||||
EcsCompPhantom::MountState(_) => sync::handle_remove::<comp::MountState>(entity, world),
|
||||
|
@ -3,9 +3,11 @@ pub mod ecs_packet;
|
||||
pub mod server;
|
||||
|
||||
// Reexports
|
||||
pub use self::client::ClientMsg;
|
||||
pub use self::ecs_packet::EcsCompPacket;
|
||||
pub use self::server::{PlayerListUpdate, RequestStateError, ServerError, ServerInfo, ServerMsg};
|
||||
pub use self::{
|
||||
client::ClientMsg,
|
||||
ecs_packet::EcsCompPacket,
|
||||
server::{PlayerListUpdate, RequestStateError, ServerError, ServerInfo, ServerMsg},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ClientState {
|
||||
|
@ -41,8 +41,8 @@ pub enum ServerMsg {
|
||||
},
|
||||
PlayerListUpdate(PlayerListUpdate),
|
||||
StateAnswer(Result<ClientState, (RequestStateError, ClientState)>),
|
||||
/// Trigger cleanup for when the client goes back to the `Registered` state from an ingame
|
||||
/// state
|
||||
/// Trigger cleanup for when the client goes back to the `Registered` state
|
||||
/// from an ingame state
|
||||
ExitIngameCleanup,
|
||||
Ping,
|
||||
Pong,
|
||||
@ -96,30 +96,35 @@ impl ServerMsg {
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tell(message: String) -> ServerMsg {
|
||||
ServerMsg::ChatMsg {
|
||||
chat_type: ChatType::Tell,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn game(message: String) -> ServerMsg {
|
||||
ServerMsg::ChatMsg {
|
||||
chat_type: ChatType::GameUpdate,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn broadcast(message: String) -> ServerMsg {
|
||||
ServerMsg::ChatMsg {
|
||||
chat_type: ChatType::Broadcast,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private(message: String) -> ServerMsg {
|
||||
ServerMsg::ChatMsg {
|
||||
chat_type: ChatType::Private,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill(message: String) -> ServerMsg {
|
||||
ServerMsg::ChatMsg {
|
||||
chat_type: ChatType::Kill,
|
||||
|
@ -12,7 +12,8 @@ pub enum ClientMsg {
|
||||
VersionInfo {},
|
||||
}
|
||||
|
||||
/// Control message type, used in [PostBox](super::PostBox) and [PostOffice](super::PostOffice) to control threads.
|
||||
/// Control message type, used in [PostBox](super::PostBox) and
|
||||
/// [PostOffice](super::PostOffice) to control threads.
|
||||
pub enum ControlMsg {
|
||||
Shutdown,
|
||||
}
|
||||
|
@ -24,21 +24,15 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Io(Arc::new(err))
|
||||
}
|
||||
fn from(err: io::Error) -> Self { Error::Io(Arc::new(err)) }
|
||||
}
|
||||
|
||||
impl From<bincode::Error> for Error {
|
||||
fn from(err: bincode::Error) -> Self {
|
||||
Error::Bincode(Arc::new(err))
|
||||
}
|
||||
fn from(err: bincode::Error) -> Self { Error::Bincode(Arc::new(err)) }
|
||||
}
|
||||
|
||||
impl From<channel::TryRecvError> for Error {
|
||||
fn from(_error: channel::TryRecvError) -> Self {
|
||||
Error::ChannelFailure
|
||||
}
|
||||
fn from(_error: channel::TryRecvError) -> Self { Error::ChannelFailure }
|
||||
}
|
||||
|
||||
pub trait PostMsg = Serialize + DeserializeOwned + 'static + Send;
|
||||
@ -63,9 +57,7 @@ impl<S: PostMsg, R: PostMsg> PostOffice<S, R> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error(&self) -> Option<Error> {
|
||||
self.error.clone()
|
||||
}
|
||||
pub fn error(&self) -> Option<Error> { self.error.clone() }
|
||||
|
||||
pub fn new_postboxes(&mut self) -> impl ExactSizeIterator<Item = PostBox<S, R>> {
|
||||
let mut new = Vec::new();
|
||||
@ -78,11 +70,11 @@ impl<S: PostMsg, R: PostMsg> PostOffice<S, R> {
|
||||
match self.listener.accept() {
|
||||
Ok((stream, _sock)) => new.push(PostBox::from_stream(stream).unwrap()),
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {},
|
||||
Err(e) => {
|
||||
self.error = Some(e.into());
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,13 +115,9 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error(&self) -> Option<Error> {
|
||||
self.error.clone()
|
||||
}
|
||||
pub fn error(&self) -> Option<Error> { self.error.clone() }
|
||||
|
||||
pub fn send_message(&mut self, msg: S) {
|
||||
let _ = self.send_tx.send(msg);
|
||||
}
|
||||
pub fn send_message(&mut self, msg: S) { let _ = self.send_tx.send(msg); }
|
||||
|
||||
pub fn next_message(&mut self) -> Option<R> {
|
||||
if self.error.is_some() {
|
||||
@ -141,7 +129,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
Err(e) => {
|
||||
self.error = Some(e);
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,11 +147,11 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
Err(e) => {
|
||||
self.error = Some(e.into());
|
||||
break;
|
||||
}
|
||||
},
|
||||
Ok(Err(e)) => {
|
||||
self.error = Some(e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,8 +174,8 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
Ok(Some(e)) | Err(e) => {
|
||||
recv_tx.send(Err(e.into())).unwrap();
|
||||
break 'work;
|
||||
}
|
||||
Ok(None) => {}
|
||||
},
|
||||
Ok(None) => {},
|
||||
}
|
||||
|
||||
// Try getting messages from the send channel.
|
||||
@ -215,13 +203,13 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
.chunks(4096)
|
||||
.map(|chunk| chunk.to_vec())
|
||||
.for_each(|chunk| outgoing_chunks.push_back(chunk))
|
||||
}
|
||||
},
|
||||
Err(channel::TryRecvError::Empty) => break,
|
||||
// Worker error
|
||||
Err(e) => {
|
||||
let _ = recv_tx.send(Err(e.into()));
|
||||
break 'work;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,21 +217,21 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
for _ in 0..1000 {
|
||||
match outgoing_chunks.pop_front() {
|
||||
Some(mut chunk) => match stream.write(&chunk) {
|
||||
Ok(n) if n == chunk.len() => {}
|
||||
Ok(n) if n == chunk.len() => {},
|
||||
Ok(n) => {
|
||||
outgoing_chunks.push_front(chunk.split_off(n));
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
// Return chunk to the queue to try again later.
|
||||
outgoing_chunks.push_front(chunk);
|
||||
break;
|
||||
}
|
||||
},
|
||||
// Worker error
|
||||
Err(e) => {
|
||||
recv_tx.send(Err(e.into())).unwrap();
|
||||
break 'work;
|
||||
}
|
||||
},
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
@ -256,12 +244,12 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
match stream.read(&mut buf) {
|
||||
Ok(n) => incoming_buf.extend_from_slice(&buf[0..n]),
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
|
||||
Err(e) if e.kind() == io::ErrorKind::Interrupted => {},
|
||||
// Worker error
|
||||
Err(e) => {
|
||||
recv_tx.send(Err(e.into())).unwrap();
|
||||
break 'work;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,14 +282,14 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
Err(err) => {
|
||||
println!("BINCODE ERROR: {:?}", err);
|
||||
recv_tx.send(Err(err.into())).unwrap()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
incoming_buf = incoming_buf.split_off(len + 9);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
use crate::{assets, comp::AllBodies};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::seq::SliceRandom;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum NpcKind {
|
||||
@ -28,12 +27,12 @@ pub const ALL_NPCS: [NpcKind; 6] = [
|
||||
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct BodyNames {
|
||||
/// The keyword used to refer to this body type (e.g. via the command console). Should be
|
||||
/// unique per body type.
|
||||
/// The keyword used to refer to this body type (e.g. via the command
|
||||
/// console). Should be unique per body type.
|
||||
pub keyword: String,
|
||||
/// A list of canonical names for NPCs with this body types (currently used when spawning this
|
||||
/// kind of NPC from the console). Going forward, these names will likely be split up by
|
||||
/// species.
|
||||
/// A list of canonical names for NPCs with this body types (currently used
|
||||
/// when spawning this kind of NPC from the console). Going forward,
|
||||
/// these names will likely be split up by species.
|
||||
pub names: Vec<String>,
|
||||
}
|
||||
|
||||
|
@ -31,17 +31,11 @@ impl<T> FromIterator<T> for Path<T> {
|
||||
}
|
||||
|
||||
impl<T> Path<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
pub fn len(&self) -> usize { self.nodes.len() }
|
||||
|
||||
pub fn start(&self) -> Option<&T> {
|
||||
self.nodes.first()
|
||||
}
|
||||
pub fn start(&self) -> Option<&T> { self.nodes.first() }
|
||||
|
||||
pub fn end(&self) -> Option<&T> {
|
||||
self.nodes.last()
|
||||
}
|
||||
pub fn end(&self) -> Option<&T> { self.nodes.last() }
|
||||
}
|
||||
|
||||
// Route: A path that can be progressed along
|
||||
@ -53,23 +47,15 @@ pub struct Route {
|
||||
}
|
||||
|
||||
impl From<Path<Vec3<i32>>> for Route {
|
||||
fn from(path: Path<Vec3<i32>>) -> Self {
|
||||
Self { path, next_idx: 0 }
|
||||
}
|
||||
fn from(path: Path<Vec3<i32>>) -> Self { Self { path, next_idx: 0 } }
|
||||
}
|
||||
|
||||
impl Route {
|
||||
pub fn path(&self) -> &Path<Vec3<i32>> {
|
||||
&self.path
|
||||
}
|
||||
pub fn path(&self) -> &Path<Vec3<i32>> { &self.path }
|
||||
|
||||
pub fn next(&self) -> Option<Vec3<i32>> {
|
||||
self.path.nodes.get(self.next_idx).copied()
|
||||
}
|
||||
pub fn next(&self) -> Option<Vec3<i32>> { self.path.nodes.get(self.next_idx).copied() }
|
||||
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.next().is_none()
|
||||
}
|
||||
pub fn is_finished(&self) -> bool { self.next().is_none() }
|
||||
|
||||
pub fn traverse<V>(&mut self, vol: &V, pos: Vec3<f32>) -> Option<Vec3<f32>>
|
||||
where
|
||||
@ -89,7 +75,8 @@ impl Route {
|
||||
}
|
||||
}
|
||||
|
||||
/// A self-contained system that attempts to chase a moving target, only performing pathfinding if necessary
|
||||
/// A self-contained system that attempts to chase a moving target, only
|
||||
/// performing pathfinding if necessary
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Chaser {
|
||||
last_search_tgt: Option<Vec3<f32>>,
|
||||
@ -258,15 +245,15 @@ where
|
||||
PathResult::Path(path) => {
|
||||
*astar = None;
|
||||
path
|
||||
}
|
||||
},
|
||||
PathResult::None(path) => {
|
||||
*astar = None;
|
||||
path
|
||||
}
|
||||
},
|
||||
PathResult::Exhausted(path) => {
|
||||
*astar = None;
|
||||
path
|
||||
}
|
||||
},
|
||||
PathResult::Pending => Path::default(),
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,7 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>, G: RayForEach> Ray<'a, V, F, G> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn until(self, f: F) -> Ray<'a, V, F, G> {
|
||||
Ray { until: f, ..self }
|
||||
}
|
||||
pub fn until(self, f: F) -> Ray<'a, V, F, G> { Ray { until: f, ..self } }
|
||||
|
||||
pub fn for_each<H: RayForEach>(self, f: H) -> Ray<'a, V, F, H> {
|
||||
Ray {
|
||||
@ -79,7 +77,7 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>, G: RayForEach> Ray<'a, V, F, G> {
|
||||
match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) {
|
||||
Ok((vox, true)) => return (dist, Ok(Some(vox))),
|
||||
Err(err) if !self.ignore_error => return (dist, Err(err)),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let deltas =
|
||||
|
@ -29,28 +29,27 @@ impl Region {
|
||||
events: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the region contains no entities and no events
|
||||
fn removable(&self) -> bool {
|
||||
self.bitset.is_empty() && self.events.is_empty()
|
||||
}
|
||||
fn removable(&self) -> bool { self.bitset.is_empty() && self.events.is_empty() }
|
||||
|
||||
fn add(&mut self, id: u32, from: Option<Vec2<i32>>) {
|
||||
self.bitset.add(id);
|
||||
self.events.push(Event::Entered(id, from));
|
||||
}
|
||||
|
||||
fn remove(&mut self, id: u32, to: Option<Vec2<i32>>) {
|
||||
self.bitset.remove(id);
|
||||
self.events.push(Event::Left(id, to));
|
||||
}
|
||||
pub fn events(&self) -> &[Event] {
|
||||
&self.events
|
||||
}
|
||||
pub fn entities(&self) -> &BitSet {
|
||||
&self.bitset
|
||||
}
|
||||
|
||||
pub fn events(&self) -> &[Event] { &self.events }
|
||||
|
||||
pub fn entities(&self) -> &BitSet { &self.bitset }
|
||||
}
|
||||
|
||||
/// How far can an entity roam outside its region before it is switched over to the neighboring one
|
||||
/// In units of blocks (i.e. world pos)
|
||||
/// How far can an entity roam outside its region before it is switched over to
|
||||
/// the neighboring one In units of blocks (i.e. world pos)
|
||||
/// Used to prevent rapid switching of entities between regions
|
||||
pub const TETHER_LENGTH: u32 = 16;
|
||||
/// Bitshift between region and world pos, i.e. log2(REGION_SIZE)
|
||||
@ -76,7 +75,8 @@ const NEIGHBOR_OFFSETS: [Vec2<i32>; 8] = [
|
||||
pub struct RegionMap {
|
||||
// Tree?
|
||||
// Sorted Vec? (binary search lookup)
|
||||
// Sort into multiple vecs (say 32) using lower bits of morton code, then binary search via upper bits? <-- sounds very promising to me (might not be super good though?)
|
||||
// Sort into multiple vecs (say 32) using lower bits of morton code, then binary search via
|
||||
// upper bits? <-- sounds very promising to me (might not be super good though?)
|
||||
regions: IndexMap<Vec2<i32>, Region, DefaultHashBuilder>,
|
||||
// If an entity isn't here it needs to be added to a region
|
||||
tracked_entities: BitSet,
|
||||
@ -99,6 +99,7 @@ impl RegionMap {
|
||||
tick: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO maintain within a system?
|
||||
// TODO special case large entities
|
||||
pub fn tick(&mut self, pos: ReadStorage<Pos>, vel: ReadStorage<Vel>, entities: Entities) {
|
||||
@ -154,13 +155,14 @@ impl RegionMap {
|
||||
// Switch
|
||||
self.entities_to_move.push((i, id, pos));
|
||||
}
|
||||
}
|
||||
// Remove any non-existant entities (or just ones that lost their position component)
|
||||
// TODO: distribute this between ticks
|
||||
},
|
||||
// Remove any non-existant entities (or just ones that lost their position
|
||||
// component) TODO: distribute this between ticks
|
||||
None => {
|
||||
// TODO: shouldn't there be a way to extract the bitset of entities with positions directly from specs?
|
||||
// TODO: shouldn't there be a way to extract the bitset of entities with
|
||||
// positions directly from specs?
|
||||
self.entities_to_remove.push((i, id));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +175,8 @@ impl RegionMap {
|
||||
}
|
||||
|
||||
// Mutate
|
||||
// Note entity moving is outside the whole loop so that the same entity is not checked twice (this may be fine though...)
|
||||
// Note entity moving is outside the whole loop so that the same entity is not
|
||||
// checked twice (this may be fine though...)
|
||||
while let Some((i, id, pos)) = self.entities_to_move.pop() {
|
||||
let (prev_key, region) = self.regions.get_index_mut(i).map(|(k, v)| (*k, v)).unwrap();
|
||||
region.remove(id, Some(Self::pos_key(pos)));
|
||||
@ -191,11 +194,13 @@ impl RegionMap {
|
||||
for key in regions_to_remove.into_iter() {
|
||||
// Check that the region is still removable
|
||||
if self.regions.get(&key).unwrap().removable() {
|
||||
// Note we have to use key's here since the index can change when others are removed
|
||||
// Note we have to use key's here since the index can change when others are
|
||||
// removed
|
||||
self.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_entity(&mut self, id: u32, pos: Vec3<i32>, from: Option<Vec2<i32>>) {
|
||||
let key = Self::pos_key(pos);
|
||||
if let Some(region) = self.regions.get_mut(&key) {
|
||||
@ -210,13 +215,13 @@ impl RegionMap {
|
||||
.unwrap()
|
||||
.add(id, None);
|
||||
}
|
||||
fn pos_key<P: Into<Vec2<i32>>>(pos: P) -> Vec2<i32> {
|
||||
pos.into().map(|e| e >> REGION_LOG2)
|
||||
}
|
||||
pub fn key_pos(key: Vec2<i32>) -> Vec2<i32> {
|
||||
key.map(|e| e << REGION_LOG2)
|
||||
}
|
||||
/// Finds the region where a given entity is located using a given position to speed up the search
|
||||
|
||||
fn pos_key<P: Into<Vec2<i32>>>(pos: P) -> Vec2<i32> { pos.into().map(|e| e >> REGION_LOG2) }
|
||||
|
||||
pub fn key_pos(key: Vec2<i32>) -> Vec2<i32> { key.map(|e| e << REGION_LOG2) }
|
||||
|
||||
/// Finds the region where a given entity is located using a given position
|
||||
/// to speed up the search
|
||||
pub fn find_region(&self, entity: specs::Entity, pos: Vec3<f32>) -> Option<Vec2<i32>> {
|
||||
let id = entity.id();
|
||||
// Compute key for most likely region
|
||||
@ -257,12 +262,15 @@ impl RegionMap {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn key_index(&self, key: Vec2<i32>) -> Option<usize> {
|
||||
self.regions.get_full(&key).map(|(i, _, _)| i)
|
||||
}
|
||||
|
||||
fn index_key(&self, index: usize) -> Option<Vec2<i32>> {
|
||||
self.regions.get_index(index).map(|(k, _)| k).copied()
|
||||
}
|
||||
|
||||
/// Adds a new region
|
||||
/// Returns the index of the region in the index map
|
||||
fn insert(&mut self, key: Vec2<i32>) -> usize {
|
||||
@ -289,15 +297,18 @@ impl RegionMap {
|
||||
|
||||
index
|
||||
}
|
||||
|
||||
/// Remove a region using its key
|
||||
fn remove(&mut self, key: Vec2<i32>) {
|
||||
if let Some(index) = self.key_index(key) {
|
||||
self.remove_index(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a region using its key
|
||||
fn remove_index(&mut self, index: usize) {
|
||||
// Remap neighbor indices for neighbors of the region that will be moved from the end of the index map
|
||||
// Remap neighbor indices for neighbors of the region that will be moved from
|
||||
// the end of the index map
|
||||
if index != self.regions.len() - 1 {
|
||||
let moved_neighbors = self
|
||||
.regions
|
||||
@ -335,10 +346,10 @@ impl RegionMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a region given a key
|
||||
pub fn get(&self, key: Vec2<i32>) -> Option<&Region> {
|
||||
self.regions.get(&key)
|
||||
}
|
||||
pub fn get(&self, key: Vec2<i32>) -> Option<&Region> { self.regions.get(&key) }
|
||||
|
||||
// Returns an iterator of (Position, Region)
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &Region)> {
|
||||
self.regions.iter().map(|(key, r)| (*key, r))
|
||||
@ -350,7 +361,8 @@ pub fn region_in_vd(key: Vec2<i32>, pos: Vec3<f32>, vd: f32) -> bool {
|
||||
let vd_extended = vd + TETHER_LENGTH as f32 * 2.0f32.sqrt();
|
||||
|
||||
let min_region_pos = RegionMap::key_pos(key).map(|e| e as f32);
|
||||
// Should be diff to closest point on the square (which can be in the middle of an edge)
|
||||
// Should be diff to closest point on the square (which can be in the middle of
|
||||
// an edge)
|
||||
let diff = (min_region_pos - Vec2::from(pos)).map(|e| {
|
||||
if e < 0.0 {
|
||||
(e + REGION_SIZE as f32).min(0.0)
|
||||
|
@ -34,10 +34,11 @@ pub struct Time(pub f64);
|
||||
#[derive(Default)]
|
||||
pub struct DeltaTime(pub f32);
|
||||
|
||||
/// At what point should we stop speeding up physics to compensate for lag? If we speed physics up
|
||||
/// too fast, we'd skip important physics events like collisions. This constant determines the
|
||||
/// upper limit. If delta time exceeds this value, the game's physics will begin to produce time
|
||||
/// lag. Ideally, we'd avoid such a situation.
|
||||
/// At what point should we stop speeding up physics to compensate for lag? If
|
||||
/// we speed physics up too fast, we'd skip important physics events like
|
||||
/// collisions. This constant determines the upper limit. If delta time exceeds
|
||||
/// this value, the game's physics will begin to produce time lag. Ideally, we'd
|
||||
/// avoid such a situation.
|
||||
const MAX_DELTA_TIME: f32 = 1.0;
|
||||
const HUMANOID_JUMP_ACCEL: f32 = 26.0;
|
||||
|
||||
@ -47,9 +48,7 @@ pub struct BlockChange {
|
||||
}
|
||||
|
||||
impl BlockChange {
|
||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
||||
self.blocks.insert(pos, block);
|
||||
}
|
||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block) { self.blocks.insert(pos, block); }
|
||||
|
||||
pub fn try_set(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
|
||||
if !self.blocks.contains_key(&pos) {
|
||||
@ -60,9 +59,7 @@ impl BlockChange {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.blocks.clear();
|
||||
}
|
||||
pub fn clear(&mut self) { self.blocks.clear(); }
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -81,8 +78,9 @@ impl TerrainChanges {
|
||||
}
|
||||
}
|
||||
|
||||
/// A type used to represent game state stored on both the client and the server. This includes
|
||||
/// things like entity components, terrain data, and global states like weather, time of day, etc.
|
||||
/// A type used to represent game state stored on both the client and the
|
||||
/// server. This includes things like entity components, terrain data, and
|
||||
/// global states like weather, time of day, etc.
|
||||
pub struct State {
|
||||
ecs: specs::World,
|
||||
// Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
|
||||
@ -101,7 +99,8 @@ impl Default for State {
|
||||
|
||||
impl State {
|
||||
/// Creates ecs world and registers all the common components and resources
|
||||
// TODO: Split up registering into server and client (e.g. move EventBus<ServerEvent> to the server)
|
||||
// TODO: Split up registering into server and client (e.g. move
|
||||
// EventBus<ServerEvent> to the server)
|
||||
fn setup_ecs_world() -> specs::World {
|
||||
let mut ecs = specs::World::new();
|
||||
// Uids for sync
|
||||
@ -197,56 +196,43 @@ impl State {
|
||||
}
|
||||
|
||||
/// Get a reference to the internal ECS world.
|
||||
pub fn ecs(&self) -> &specs::World {
|
||||
&self.ecs
|
||||
}
|
||||
pub fn ecs(&self) -> &specs::World { &self.ecs }
|
||||
|
||||
/// Get a mutable reference to the internal ECS world.
|
||||
pub fn ecs_mut(&mut self) -> &mut specs::World {
|
||||
&mut self.ecs
|
||||
}
|
||||
pub fn ecs_mut(&mut self) -> &mut specs::World { &mut self.ecs }
|
||||
|
||||
/// Get a reference to the `TerrainChanges` structure of the state. This contains
|
||||
/// information about terrain state that has changed since the last game tick.
|
||||
pub fn terrain_changes(&self) -> Fetch<TerrainChanges> {
|
||||
self.ecs.read_resource()
|
||||
}
|
||||
/// Get a reference to the `TerrainChanges` structure of the state. This
|
||||
/// contains information about terrain state that has changed since the
|
||||
/// last game tick.
|
||||
pub fn terrain_changes(&self) -> Fetch<TerrainChanges> { self.ecs.read_resource() }
|
||||
|
||||
/// Get the current in-game time of day.
|
||||
///
|
||||
/// Note that this should not be used for physics, animations or other such localised timings.
|
||||
pub fn get_time_of_day(&self) -> f64 {
|
||||
self.ecs.read_resource::<TimeOfDay>().0
|
||||
}
|
||||
/// Note that this should not be used for physics, animations or other such
|
||||
/// localised timings.
|
||||
pub fn get_time_of_day(&self) -> f64 { self.ecs.read_resource::<TimeOfDay>().0 }
|
||||
|
||||
/// Get the current in-game time.
|
||||
///
|
||||
/// Note that this does not correspond to the time of day.
|
||||
pub fn get_time(&self) -> f64 {
|
||||
self.ecs.read_resource::<Time>().0
|
||||
}
|
||||
pub fn get_time(&self) -> f64 { self.ecs.read_resource::<Time>().0 }
|
||||
|
||||
/// Get the current delta time.
|
||||
pub fn get_delta_time(&self) -> f32 {
|
||||
self.ecs.read_resource::<DeltaTime>().0
|
||||
}
|
||||
pub fn get_delta_time(&self) -> f32 { self.ecs.read_resource::<DeltaTime>().0 }
|
||||
|
||||
/// Get a reference to this state's terrain.
|
||||
pub fn terrain(&self) -> Fetch<TerrainGrid> {
|
||||
self.ecs.read_resource()
|
||||
}
|
||||
pub fn terrain(&self) -> Fetch<TerrainGrid> { self.ecs.read_resource() }
|
||||
|
||||
/// Get a writable reference to this state's terrain.
|
||||
pub fn terrain_mut(&self) -> FetchMut<TerrainGrid> {
|
||||
self.ecs.write_resource()
|
||||
}
|
||||
pub fn terrain_mut(&self) -> FetchMut<TerrainGrid> { self.ecs.write_resource() }
|
||||
|
||||
/// Set a block in this state's terrain.
|
||||
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
|
||||
self.ecs.write_resource::<BlockChange>().set(pos, block);
|
||||
}
|
||||
|
||||
/// Set a block in this state's terrain. Will return `None` if the block has already been modified this tick.
|
||||
/// Set a block in this state's terrain. Will return `None` if the block has
|
||||
/// already been modified this tick.
|
||||
pub fn try_set_block(&mut self, pos: Vec3<i32>, block: Block) -> Option<()> {
|
||||
self.ecs.write_resource::<BlockChange>().try_set(pos, block)
|
||||
}
|
||||
@ -284,7 +270,8 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the chunk with the given key from this state's terrain, if it exists.
|
||||
/// Remove the chunk with the given key from this state's terrain, if it
|
||||
/// exists.
|
||||
pub fn remove_chunk(&mut self, key: Vec2<i32>) {
|
||||
if self
|
||||
.ecs
|
||||
@ -306,7 +293,8 @@ impl State {
|
||||
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
|
||||
|
||||
// Update delta time.
|
||||
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events.
|
||||
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping
|
||||
// important physics events.
|
||||
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
||||
|
||||
// Run RegionMap tick to update entity region occupancy
|
||||
@ -348,7 +336,7 @@ impl State {
|
||||
if let Some(vel) = velocities.get_mut(entity) {
|
||||
vel.0.z = HUMANOID_JUMP_ACCEL;
|
||||
}
|
||||
}
|
||||
},
|
||||
LocalEvent::WallLeap { entity, wall_dir } => {
|
||||
if let (Some(vel), Some(_controller)) =
|
||||
(velocities.get_mut(entity), controllers.get_mut(entity))
|
||||
@ -363,7 +351,7 @@ impl State {
|
||||
vel.0.z = HUMANOID_JUMP_ACCEL * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
LocalEvent::Boost {
|
||||
entity,
|
||||
vel: extra_vel,
|
||||
@ -371,7 +359,7 @@ impl State {
|
||||
if let Some(vel) = velocities.get_mut(entity) {
|
||||
vel.0 += extra_vel;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Note: Currently only one-way sync is supported until a usecase for two-way sync arises
|
||||
// Note: Currently only one-way sync is supported until a usecase for two-way
|
||||
// sync arises
|
||||
mod packet;
|
||||
mod sync_ext;
|
||||
mod track;
|
||||
|
@ -8,9 +8,9 @@ use std::{
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Implemented by type that carries component data for insertion and modification
|
||||
/// The assocatied `Phantom` type only carries information about which component type is of
|
||||
/// interest and is used to transmit deletion events
|
||||
/// Implemented by type that carries component data for insertion and
|
||||
/// modification The assocatied `Phantom` type only carries information about
|
||||
/// which component type is of interest and is used to transmit deletion events
|
||||
pub trait CompPacket: Clone + Debug + Send + 'static {
|
||||
type Phantom: Clone + Debug + Serialize + DeserializeOwned;
|
||||
|
||||
@ -64,9 +64,8 @@ impl<P: CompPacket> Default for StatePackage<P> {
|
||||
}
|
||||
|
||||
impl<P: CompPacket> StatePackage<P> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn new() -> Self { Self::default() }
|
||||
|
||||
pub fn with_entities<C: Component + Clone + Send + Sync>(
|
||||
mut self,
|
||||
mut entities: Vec<EntityPackage<P>>,
|
||||
@ -74,6 +73,7 @@ impl<P: CompPacket> StatePackage<P> {
|
||||
self.entities.append(&mut entities);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_entity(mut self, entry: EntityPackage<P>) -> Self {
|
||||
self.entities.push(entry);
|
||||
self
|
||||
@ -105,6 +105,7 @@ impl<P: CompPacket> SyncPackage<P> {
|
||||
deleted_entities,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_component<'a, C: Component + Clone + Send + Sync>(
|
||||
mut self,
|
||||
uids: &ReadStorage<'a, Uid>,
|
||||
|
@ -37,6 +37,7 @@ impl WorldSyncExt for specs::World {
|
||||
// TODO: Consider only having allocator server side for now
|
||||
self.insert(UidAllocator::new());
|
||||
}
|
||||
|
||||
fn register_synced<C: specs::Component + Clone + Send + Sync>(&mut self)
|
||||
where
|
||||
C::Storage: Default + specs::storage::Tracked,
|
||||
@ -44,6 +45,7 @@ impl WorldSyncExt for specs::World {
|
||||
self.register::<C>();
|
||||
self.register_tracker::<C>();
|
||||
}
|
||||
|
||||
fn register_tracker<C: specs::Component + Clone + Send + Sync>(&mut self)
|
||||
where
|
||||
C::Storage: Default + specs::storage::Tracked,
|
||||
@ -126,13 +128,13 @@ impl WorldSyncExt for specs::World {
|
||||
match update {
|
||||
CompUpdateKind::Inserted(packet) => {
|
||||
packet.apply_insert(entity, self);
|
||||
}
|
||||
},
|
||||
CompUpdateKind::Modified(packet) => {
|
||||
packet.apply_modify(entity, self);
|
||||
}
|
||||
},
|
||||
CompUpdateKind::Removed(phantom) => {
|
||||
P::apply_remove(phantom, entity, self);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,6 +161,6 @@ fn create_entity_with_uid(specs_world: &mut specs::World, entity_uid: u64) -> sp
|
||||
.write_resource::<UidAllocator>()
|
||||
.allocate(entity_builder.entity, Some(entity_uid));
|
||||
entity_builder.with(uid).build()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -28,15 +28,13 @@ where
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn inserted(&self) -> &BitSet {
|
||||
&self.inserted
|
||||
}
|
||||
pub fn modified(&self) -> &BitSet {
|
||||
&self.modified
|
||||
}
|
||||
pub fn removed(&self) -> &BitSet {
|
||||
&self.removed
|
||||
}
|
||||
|
||||
pub fn inserted(&self) -> &BitSet { &self.inserted }
|
||||
|
||||
pub fn modified(&self) -> &BitSet { &self.modified }
|
||||
|
||||
pub fn removed(&self) -> &BitSet { &self.removed }
|
||||
|
||||
pub fn record_changes<'a>(&mut self, storage: &specs::ReadStorage<'a, C>) {
|
||||
self.inserted.clear();
|
||||
self.modified.clear();
|
||||
@ -49,20 +47,21 @@ where
|
||||
self.removed.remove(*id);
|
||||
self.modified.remove(*id);
|
||||
self.inserted.add(*id);
|
||||
}
|
||||
},
|
||||
specs::storage::ComponentEvent::Modified(id) => {
|
||||
// We don't care about modification if the component was just added
|
||||
if !self.inserted.contains(*id) {
|
||||
debug_assert!(!self.removed.contains(*id)); // Theoretically impossible
|
||||
self.modified.add(*id);
|
||||
}
|
||||
}
|
||||
},
|
||||
specs::storage::ComponentEvent::Removed(id) => {
|
||||
// Don't need to know that it was inserted/modified if it was subsequently removed
|
||||
// Don't need to know that it was inserted/modified if it was subsequently
|
||||
// removed
|
||||
self.inserted.remove(*id);
|
||||
self.modified.remove(*id);
|
||||
self.removed.add(*id);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -10,21 +10,15 @@ use std::{collections::HashMap, fmt, u64};
|
||||
pub struct Uid(pub u64);
|
||||
|
||||
impl Into<u64> for Uid {
|
||||
fn into(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
fn into(self) -> u64 { self.0 }
|
||||
}
|
||||
|
||||
impl From<u64> for Uid {
|
||||
fn from(uid: u64) -> Self {
|
||||
Self(uid)
|
||||
}
|
||||
fn from(uid: u64) -> Self { Self(uid) }
|
||||
}
|
||||
|
||||
impl fmt::Display for Uid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
|
||||
}
|
||||
|
||||
impl Component for Uid {
|
||||
@ -32,12 +26,10 @@ impl Component for Uid {
|
||||
}
|
||||
|
||||
impl Marker for Uid {
|
||||
type Identifier = u64;
|
||||
type Allocator = UidAllocator;
|
||||
type Identifier = u64;
|
||||
|
||||
fn id(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
fn id(&self) -> u64 { self.0 }
|
||||
|
||||
fn update(&mut self, update: Self) {
|
||||
assert_eq!(self.0, update.0);
|
||||
@ -56,16 +48,14 @@ impl UidAllocator {
|
||||
mapping: HashMap::new(),
|
||||
}
|
||||
}
|
||||
// Useful for when a single entity is deleted because it doesn't reconstruct the entire hashmap
|
||||
pub fn remove_entity(&mut self, id: u64) -> Option<Entity> {
|
||||
self.mapping.remove(&id)
|
||||
}
|
||||
|
||||
// Useful for when a single entity is deleted because it doesn't reconstruct the
|
||||
// entire hashmap
|
||||
pub fn remove_entity(&mut self, id: u64) -> Option<Entity> { self.mapping.remove(&id) }
|
||||
}
|
||||
|
||||
impl Default for UidAllocator {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
impl MarkerAllocator<Uid> for UidAllocator {
|
||||
@ -79,9 +69,7 @@ impl MarkerAllocator<Uid> for UidAllocator {
|
||||
Uid(id)
|
||||
}
|
||||
|
||||
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> {
|
||||
self.mapping.get(&id).copied()
|
||||
}
|
||||
fn retrieve_entity_internal(&self, id: u64) -> Option<Entity> { self.mapping.get(&id).copied() }
|
||||
|
||||
fn maintain(&mut self, entities: &EntitiesRes, storage: &ReadStorage<Uid>) {
|
||||
self.mapping = (entities, storage)
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::terrain::TerrainGrid;
|
||||
use crate::{
|
||||
comp::{self, agent::Activity, Agent, Alignment, Controller, MountState, Pos, Stats},
|
||||
path::Chaser,
|
||||
state::Time,
|
||||
sync::UidAllocator,
|
||||
terrain::TerrainGrid,
|
||||
vol::ReadVol,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
@ -124,7 +124,7 @@ impl<'a> System<'a> for Sys {
|
||||
if thread_rng().gen::<f32>() < 0.1 {
|
||||
choose_target = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Activity::Follow(target, chaser) => {
|
||||
if let (Some(tgt_pos), _tgt_stats) =
|
||||
(positions.get(*target), stats.get(*target))
|
||||
@ -146,7 +146,7 @@ impl<'a> System<'a> for Sys {
|
||||
} else {
|
||||
do_idle = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Activity::Attack {
|
||||
target,
|
||||
chaser,
|
||||
@ -207,7 +207,7 @@ impl<'a> System<'a> for Sys {
|
||||
} else {
|
||||
do_idle = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,8 +215,8 @@ impl<'a> System<'a> for Sys {
|
||||
agent.activity = Activity::Idle(Vec2::zero());
|
||||
}
|
||||
|
||||
// Choose a new target to attack: only go out of our way to attack targets we are
|
||||
// hostile toward!
|
||||
// Choose a new target to attack: only go out of our way to attack targets we
|
||||
// are hostile toward!
|
||||
if choose_target {
|
||||
// Search for new targets (this looks expensive, but it's only run occasionally)
|
||||
// TODO: Replace this with a better system that doesn't consider *all* entities
|
||||
@ -243,7 +243,8 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Activity overrides (in reverse order of priority: most important goes last!) ---
|
||||
// --- Activity overrides (in reverse order of priority: most important goes
|
||||
// last!) ---
|
||||
|
||||
// Attack a target that's attacking us
|
||||
if let Some(stats) = stats.get(entity) {
|
||||
|
@ -17,7 +17,8 @@ const ATTACK_RANGE: f32 = 3.5;
|
||||
const ATTACK_ANGLE: f32 = 45.0;
|
||||
const BLOCK_ANGLE: f32 = 180.0;
|
||||
|
||||
/// This system is responsible for handling accepted inputs like moving or attacking
|
||||
/// This system is responsible for handling accepted inputs like moving or
|
||||
/// attacking
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
|
@ -19,8 +19,8 @@ use vek::*;
|
||||
const CHARGE_COST: i32 = 200;
|
||||
|
||||
/// # Controller System
|
||||
/// #### Responsible for validating controller inputs and setting new Character States
|
||||
/// ----
|
||||
/// #### Responsible for validating controller inputs and setting new Character
|
||||
/// States ----
|
||||
///
|
||||
/// **Writes:**
|
||||
/// `CharacterState`, `ControllerInputs`
|
||||
@ -28,8 +28,8 @@ const CHARGE_COST: i32 = 200;
|
||||
/// **Reads:**
|
||||
/// `Stats`, `Vel`, `PhysicsState`, `Uid`, `Mounting`
|
||||
///
|
||||
/// _TODO: Join ActionStates and MovementStates into one and have a handle() trait / fn?_
|
||||
/// _TODO: Move weapon action to trait fn?_
|
||||
/// _TODO: Join ActionStates and MovementStates into one and have a handle()
|
||||
/// trait / fn?_ _TODO: Move weapon action to trait fn?_
|
||||
pub struct Sys;
|
||||
|
||||
impl Sys {
|
||||
@ -89,14 +89,14 @@ impl Sys {
|
||||
applied: false, // We don't want to do a melee attack
|
||||
}
|
||||
//character.action
|
||||
}
|
||||
},
|
||||
item::Tool::Debug(item::Debug::Boost) => {
|
||||
local_emitter.emit(LocalEvent::Boost {
|
||||
entity,
|
||||
vel: inputs.look_dir * 7.0,
|
||||
});
|
||||
character.action
|
||||
}
|
||||
},
|
||||
|
||||
item::Tool::Debug(item::Debug::Possess)
|
||||
if character.action.is_action_finished() =>
|
||||
@ -132,9 +132,9 @@ impl Sys {
|
||||
_ => {
|
||||
// Return the new Wield action
|
||||
character.action
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
// Without a weapon
|
||||
None => {
|
||||
// Attack
|
||||
@ -146,7 +146,7 @@ impl Sys {
|
||||
} else {
|
||||
character.action
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => character.action,
|
||||
}
|
||||
}
|
||||
@ -223,7 +223,7 @@ impl Sys {
|
||||
});
|
||||
|
||||
character.action
|
||||
}
|
||||
},
|
||||
|
||||
// All other weapons block
|
||||
_ if character.action.is_action_finished() => Block {
|
||||
@ -232,7 +232,7 @@ impl Sys {
|
||||
|
||||
_ => character.action,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => character.action,
|
||||
}
|
||||
@ -256,6 +256,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Mounting>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
@ -353,15 +354,15 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
||||
}
|
||||
}
|
||||
},
|
||||
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
||||
ControlEvent::InventoryManip(manip) => {
|
||||
server_emitter.emit(ServerEvent::InventoryManip(entity, manip))
|
||||
} /*ControlEvent::Respawn => {
|
||||
if state.is_dead {
|
||||
server_emitter.emit(ServerEvent::Respawn(entity)),
|
||||
}
|
||||
}*/
|
||||
}, /*ControlEvent::Respawn => {
|
||||
if state.is_dead {
|
||||
server_emitter.emit(ServerEvent::Respawn(entity)),
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,7 +380,7 @@ impl<'a> System<'a> for Sys {
|
||||
(_, Jump) => {
|
||||
character.movement = Fall;
|
||||
local_emitter.emit(LocalEvent::Jump(entity));
|
||||
}
|
||||
},
|
||||
// Charging + Any Movement, prioritizes finishing charge
|
||||
// over movement states
|
||||
(Charge { time_left }, _) => {
|
||||
@ -404,7 +405,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
character.action = try_wield(stats);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Rolling + Any Movement, prioritizes finishing charge
|
||||
// over movement states
|
||||
(
|
||||
@ -428,7 +429,7 @@ impl<'a> System<'a> for Sys {
|
||||
was_wielding,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Any Action + Falling
|
||||
(action_state, Fall) => {
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
@ -464,21 +465,21 @@ impl<'a> System<'a> for Sys {
|
||||
if inputs.toggle_wield.is_just_pressed() {
|
||||
character.action = Idle;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Try to wield if any of buttons pressed
|
||||
Idle => {
|
||||
if inputs.primary.is_pressed() || inputs.secondary.is_pressed() {
|
||||
character.action = try_wield(stats);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Cancel blocks
|
||||
Block { .. } => {
|
||||
character.action = try_wield(stats);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
// Don't change action
|
||||
Charge { .. } | Roll { .. } => {}
|
||||
Charge { .. } | Roll { .. } => {},
|
||||
}
|
||||
if inputs.primary.is_pressed() {
|
||||
character.action = Self::handle_primary(
|
||||
@ -501,7 +502,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut local_emitter,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Any Action + Swimming
|
||||
(_action_state, Swim) => {
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
@ -530,7 +531,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut local_emitter,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Blocking, restricted look_dir compared to other states
|
||||
(Block { .. }, Stand) | (Block { .. }, Run) => {
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
@ -556,7 +557,7 @@ impl<'a> System<'a> for Sys {
|
||||
character.movement = Fall;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Standing and Running states, typical states :shrug:
|
||||
(action_state, Run) | (action_state, Stand) => {
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
@ -639,12 +640,12 @@ impl<'a> System<'a> for Sys {
|
||||
character.action = Idle;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Idle => {
|
||||
character.action = try_wield(stats);
|
||||
continue;
|
||||
}
|
||||
Charge { .. } | Roll { .. } | Block { .. } => {}
|
||||
},
|
||||
Charge { .. } | Roll { .. } | Block { .. } => {},
|
||||
}
|
||||
}
|
||||
if inputs.primary.is_pressed() {
|
||||
@ -668,7 +669,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut local_emitter,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Sitting
|
||||
(_, Sit) => {
|
||||
character.action = Idle;
|
||||
@ -686,7 +687,7 @@ impl<'a> System<'a> for Sys {
|
||||
if !physics.on_ground {
|
||||
character.movement = Fall;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Any Action + Gliding, shouldnt care about action,
|
||||
// because should be Idle
|
||||
(_, Glide) => {
|
||||
@ -701,7 +702,7 @@ impl<'a> System<'a> for Sys {
|
||||
if physics.on_ground {
|
||||
character.movement = Stand
|
||||
}
|
||||
}
|
||||
},
|
||||
// Any Action + Climbing, shouldnt care about action,
|
||||
// because should be Idle
|
||||
(_, Climb) => {
|
||||
@ -716,21 +717,17 @@ impl<'a> System<'a> for Sys {
|
||||
if physics.on_ground {
|
||||
character.movement = Stand;
|
||||
}
|
||||
} // In case of adding new states
|
||||
// (_, _) => {
|
||||
// println!("UNKNOWN STATE");
|
||||
// character.action = Idle;
|
||||
// character.movement = Fall;
|
||||
// }
|
||||
}, /* In case of adding new states
|
||||
* (_, _) => {
|
||||
* println!("UNKNOWN STATE");
|
||||
* character.action = Idle;
|
||||
* character.movement = Fall;
|
||||
* } */
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn can_glide(body: &Body) -> bool {
|
||||
body.is_humanoid()
|
||||
}
|
||||
fn can_glide(body: &Body) -> bool { body.is_humanoid() }
|
||||
|
||||
fn can_climb(body: &Body) -> bool {
|
||||
body.is_humanoid()
|
||||
}
|
||||
fn can_climb(body: &Body) -> bool { body.is_humanoid() }
|
||||
|
@ -29,17 +29,13 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[]);
|
||||
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[CONTROLLER_SYS]);
|
||||
dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
|
||||
dispatch_builder.add(
|
||||
phys::Sys,
|
||||
PHYS_SYS,
|
||||
&[
|
||||
CONTROLLER_SYS,
|
||||
MOUNT_SYS,
|
||||
MOVEMENT_SYS,
|
||||
COMBAT_SYS,
|
||||
STATS_SYS,
|
||||
],
|
||||
);
|
||||
dispatch_builder.add(phys::Sys, PHYS_SYS, &[
|
||||
CONTROLLER_SYS,
|
||||
MOUNT_SYS,
|
||||
MOVEMENT_SYS,
|
||||
COMBAT_SYS,
|
||||
STATS_SYS,
|
||||
]);
|
||||
dispatch_builder.add(projectile::Sys, PROJECTILE_SYS, &[PHYS_SYS]);
|
||||
dispatch_builder.add(cleanup::Sys, CLEANUP_SYS, &[PHYS_SYS]);
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
// Mounted entities.
|
||||
for (entity, mut mount_states) in (&entities, &mut mount_state.restrict_mut()).join() {
|
||||
match mount_states.get_unchecked() {
|
||||
MountState::Unmounted => {}
|
||||
MountState::Unmounted => {},
|
||||
MountState::MountedBy(mounter_uid) => {
|
||||
// Note: currently controller events are not passed through since none of them
|
||||
// are currently relevant to controlling the mounted entity
|
||||
@ -68,7 +68,7 @@ impl<'a> System<'a> for Sys {
|
||||
} else {
|
||||
*(mount_states.get_mut_unchecked()) = MountState::Unmounted;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,29 +147,29 @@ impl<'a> System<'a> for Sys {
|
||||
< (BASE_HUMANOID_SPEED + stats.fitness as f32 * 50.0).powf(2.0) =>
|
||||
{
|
||||
BASE_HUMANOID_ACCEL
|
||||
}
|
||||
},
|
||||
(false, Climb)
|
||||
if vel.0.magnitude_squared() < BASE_HUMANOID_SPEED.powf(2.0) =>
|
||||
{
|
||||
BASE_HUMANOID_CLIMB_ACCEL
|
||||
}
|
||||
},
|
||||
(false, Glide) if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) => {
|
||||
GLIDE_ACCEL
|
||||
}
|
||||
},
|
||||
(false, Fall) | (false, Jump)
|
||||
if vel.0.magnitude_squared()
|
||||
< (BASE_HUMANOID_AIR_SPEED + stats.fitness as f32 * 10.0)
|
||||
.powf(2.0) =>
|
||||
{
|
||||
BASE_HUMANOID_AIR_ACCEL
|
||||
}
|
||||
},
|
||||
(false, Swim)
|
||||
if vel.0.magnitude_squared()
|
||||
< (BASE_HUMANOID_WATER_SPEED + stats.fitness as f32 * 30.0)
|
||||
.powf(2.0) =>
|
||||
{
|
||||
BASE_HUMANOID_WATER_ACCEL + stats.fitness as f32 * 10.0
|
||||
}
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
use {
|
||||
crate::{
|
||||
comp::{Body, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, Scale, Sticky, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
state::DeltaTime,
|
||||
sync::Uid,
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::ReadVol,
|
||||
},
|
||||
specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage},
|
||||
vek::*,
|
||||
use crate::{
|
||||
comp::{Body, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, Scale, Sticky, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
state::DeltaTime,
|
||||
sync::Uid,
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
pub const GRAVITY: f32 = 9.81 * 7.0;
|
||||
const BOUYANCY: f32 = 0.0;
|
||||
@ -150,7 +148,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec3::zero()
|
||||
};
|
||||
|
||||
// Function for determining whether the player at a specific position collides with the ground
|
||||
// Function for determining whether the player at a specific position collides
|
||||
// with the ground
|
||||
let collision_with = |pos: Vec3<f32>, hit: fn(&Block) -> bool, near_iter| {
|
||||
for (i, j, k) in near_iter {
|
||||
let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k);
|
||||
@ -199,7 +198,8 @@ impl<'a> System<'a> for Sys {
|
||||
max: pos.0 + Vec3::new(player_rad, player_rad, player_height),
|
||||
};
|
||||
|
||||
// Determine the block that we are colliding with most (based on minimum collision axis)
|
||||
// Determine the block that we are colliding with most (based on minimum
|
||||
// collision axis)
|
||||
let (_block_pos, block_aabb) = near_iter
|
||||
.clone()
|
||||
// Calculate the block's position in world space
|
||||
@ -235,7 +235,8 @@ impl<'a> System<'a> for Sys {
|
||||
// Find the intrusion vector of the collision
|
||||
let dir = player_aabb.collision_vector_with_aabb(block_aabb);
|
||||
|
||||
// Determine an appropriate resolution vector (i.e: the minimum distance needed to push out of the block)
|
||||
// Determine an appropriate resolution vector (i.e: the minimum distance needed
|
||||
// to push out of the block)
|
||||
let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
|
||||
let resolve_dir = -dir.map(|e| {
|
||||
if e.abs().to_bits() == max_axis.to_bits() {
|
||||
@ -254,8 +255,8 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// When the resolution direction is non-vertical, we must be colliding with a wall
|
||||
// If the space above is free...
|
||||
// When the resolution direction is non-vertical, we must be colliding with a
|
||||
// wall If the space above is free...
|
||||
if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), |vox| vox.is_solid(), near_iter.clone())
|
||||
// ...and we're being pushed out horizontally...
|
||||
&& resolve_dir.z == 0.0
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{projectile, /*HealthChange,*/ HealthSource, Ori, PhysicsState, Projectile, Vel},
|
||||
comp::{projectile, HealthSource, Ori, PhysicsState, Projectile, Vel},
|
||||
event::{EventBus, ServerEvent},
|
||||
state::DeltaTime,
|
||||
};
|
||||
@ -50,7 +50,7 @@ impl<'a> System<'a> for Sys {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
}),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ impl<'a> System<'a> for Sys {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
}),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -72,14 +72,14 @@ impl<'a> System<'a> for Sys {
|
||||
match effect {
|
||||
projectile::Effect::Damage(change) => {
|
||||
server_emitter.emit(ServerEvent::Damage { uid: other, change })
|
||||
}
|
||||
},
|
||||
projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
}),
|
||||
projectile::Effect::Possess => server_emitter
|
||||
.emit(ServerEvent::Possess(projectile.owner.into(), other)),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -32,7 +32,8 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
stats.set_event_emission(true);
|
||||
|
||||
// Mutates all stats every tick causing the server to resend this component for every entity every tick
|
||||
// Mutates all stats every tick causing the server to resend this component for
|
||||
// every entity every tick
|
||||
for (entity, character_state, mut stats, mut energy) in (
|
||||
&entities,
|
||||
&character_states,
|
||||
@ -88,13 +89,13 @@ impl<'a> System<'a> for Sys {
|
||||
);
|
||||
energy.regen_rate += ENERGY_REGEN_ACCEL * dt.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
// All other states do not regen and set the rate back to zero.
|
||||
_ => {
|
||||
if energy.get_unchecked().regen_rate != 0.0 {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,17 +209,13 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> BlockKind {
|
||||
self.kind
|
||||
}
|
||||
pub fn kind(&self) -> BlockKind { self.kind }
|
||||
}
|
||||
|
||||
impl Deref for Block {
|
||||
type Target = BlockKind;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.kind
|
||||
}
|
||||
fn deref(&self) -> &Self::Target { &self.kind }
|
||||
}
|
||||
|
||||
impl Vox for Block {
|
||||
@ -230,9 +226,7 @@ impl Vox for Block {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_air()
|
||||
}
|
||||
fn is_empty(&self) -> bool { self.is_air() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -55,21 +55,15 @@ impl<V: Vox, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn meta(&self) -> &M {
|
||||
&self.meta
|
||||
}
|
||||
pub fn meta(&self) -> &M { &self.meta }
|
||||
|
||||
pub fn get_min_z(&self) -> i32 {
|
||||
self.z_offset
|
||||
}
|
||||
pub fn get_min_z(&self) -> i32 { self.z_offset }
|
||||
|
||||
pub fn get_max_z(&self) -> i32 {
|
||||
self.z_offset + (self.sub_chunks.len() as u32 * SubChunkSize::<S>::SIZE.z) as i32
|
||||
}
|
||||
|
||||
pub fn sub_chunks_len(&self) -> usize {
|
||||
self.sub_chunks.len()
|
||||
}
|
||||
pub fn sub_chunks_len(&self) -> usize { self.sub_chunks.len() }
|
||||
|
||||
// Returns the index (in self.sub_chunks) of the SubChunk that contains
|
||||
// layer z; note that this index changes when more SubChunks are prepended
|
||||
@ -85,14 +79,12 @@ impl<V: Vox, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
}
|
||||
|
||||
// Returns the z offset of the sub_chunk that contains layer z
|
||||
fn sub_chunk_min_z(&self, z: i32) -> i32 {
|
||||
z - self.sub_chunk_z(z)
|
||||
}
|
||||
fn sub_chunk_min_z(&self, z: i32) -> i32 { z - self.sub_chunk_z(z) }
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> BaseVol for Chonk<V, S, M> {
|
||||
type Vox = V;
|
||||
type Error = ChonkError;
|
||||
type Vox = V;
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> RectRasterableVol for Chonk<V, S, M> {
|
||||
@ -196,7 +188,7 @@ impl<V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkPosIter<V, S, M> {
|
||||
None => return None,
|
||||
Some((sub_chunk_min_z, lb, ub)) => {
|
||||
self.opt_inner = Some((sub_chunk_min_z, SubChunk::<V, S, M>::pos_iter(lb, ub)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,7 +242,7 @@ impl<'a, V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkVolIter<'a, V, S, M
|
||||
)
|
||||
};
|
||||
self.opt_inner = Some((sub_chunk_min_z, inner));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,9 +32,7 @@ pub struct TerrainChunkMeta {
|
||||
}
|
||||
|
||||
impl TerrainChunkMeta {
|
||||
pub fn new(name: Option<String>, biome: BiomeKind) -> Self {
|
||||
Self { name, biome }
|
||||
}
|
||||
pub fn new(name: Option<String>, biome: BiomeKind) -> Self { Self { name, biome } }
|
||||
|
||||
pub fn void() -> Self {
|
||||
Self {
|
||||
@ -50,9 +48,7 @@ impl TerrainChunkMeta {
|
||||
.unwrap_or("Wilderness")
|
||||
}
|
||||
|
||||
pub fn biome(&self) -> BiomeKind {
|
||||
self.biome
|
||||
}
|
||||
pub fn biome(&self) -> BiomeKind { self.biome }
|
||||
}
|
||||
|
||||
// Terrain type aliases
|
||||
|
@ -5,8 +5,7 @@ use crate::{
|
||||
volumes::dyna::{Dyna, DynaError},
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::{fs::File, io::BufReader};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
@ -28,9 +27,7 @@ pub enum StructureBlock {
|
||||
}
|
||||
|
||||
impl Vox for StructureBlock {
|
||||
fn empty() -> Self {
|
||||
StructureBlock::None
|
||||
}
|
||||
fn empty() -> Self { StructureBlock::None }
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
@ -69,14 +66,12 @@ impl Structure {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_kind(&self) -> BlockKind {
|
||||
self.default_kind
|
||||
}
|
||||
pub fn default_kind(&self) -> BlockKind { self.default_kind }
|
||||
}
|
||||
|
||||
impl BaseVol for Structure {
|
||||
type Vox = StructureBlock;
|
||||
type Error = StructureError;
|
||||
type Vox = StructureBlock;
|
||||
}
|
||||
|
||||
impl ReadVol for Structure {
|
||||
@ -91,6 +86,7 @@ impl ReadVol for Structure {
|
||||
|
||||
impl Asset for Structure {
|
||||
const ENDINGS: &'static [&'static str] = &["vox"];
|
||||
|
||||
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
|
||||
let dot_vox_data = DotVoxData::parse(buf_reader)?;
|
||||
|
||||
@ -127,7 +123,7 @@ impl Asset for Structure {
|
||||
.copied()
|
||||
.unwrap_or_else(|| Rgb::broadcast(0));
|
||||
StructureBlock::Normal(color)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let _ = vol.set(
|
||||
|
@ -130,7 +130,8 @@ pub fn saturate_srgb(col: Rgb<f32>, value: f32) -> Rgb<f32> {
|
||||
linear_to_srgb(hsv_to_rgb(hsv).map(|e| e.min(1.0).max(0.0)))
|
||||
}
|
||||
|
||||
/// Preserves the luma of one color while changing its chromaticty to match the other
|
||||
/// Preserves the luma of one color while changing its chromaticty to match the
|
||||
/// other
|
||||
#[inline(always)]
|
||||
pub fn chromify_srgb(luma: Rgb<f32>, chroma: Rgb<f32>) -> Rgb<f32> {
|
||||
let l = rgb_to_xyy(srgb_to_linear(luma)).z;
|
||||
|
@ -2,8 +2,8 @@ use crate::ray::Ray;
|
||||
use std::fmt::Debug;
|
||||
use vek::*;
|
||||
|
||||
/// Used to specify a volume's compile-time size. This exists as a substitute until const generics
|
||||
/// are implemented.
|
||||
/// Used to specify a volume's compile-time size. This exists as a substitute
|
||||
/// until const generics are implemented.
|
||||
pub trait VolSize: Clone {
|
||||
const SIZE: Vec3<u32>;
|
||||
}
|
||||
@ -17,13 +17,7 @@ pub trait Vox: Sized + Clone + PartialEq {
|
||||
fn empty() -> Self;
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
fn or(self, other: Self) -> Self {
|
||||
if self.is_empty() {
|
||||
other
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
fn or(self, other: Self) -> Self { if self.is_empty() { other } else { self } }
|
||||
}
|
||||
|
||||
/// A volume that contains voxel data.
|
||||
@ -35,8 +29,8 @@ pub trait BaseVol {
|
||||
/// Implementing `BaseVol` for any `&'a BaseVol` makes it possible to implement
|
||||
/// `IntoVolIterator` for references.
|
||||
impl<'a, T: BaseVol> BaseVol for &'a T {
|
||||
type Vox = T::Vox;
|
||||
type Error = T::Error;
|
||||
type Vox = T::Vox;
|
||||
}
|
||||
|
||||
// Utility types
|
||||
@ -50,9 +44,7 @@ pub trait SizedVol: BaseVol {
|
||||
fn upper_bound(&self) -> Vec3<i32>;
|
||||
|
||||
/// Returns the size of the volume.
|
||||
fn size(&self) -> Vec3<u32> {
|
||||
(self.upper_bound() - self.lower_bound()).map(|e| e as u32)
|
||||
}
|
||||
fn size(&self) -> Vec3<u32> { (self.upper_bound() - self.lower_bound()).map(|e| e as u32) }
|
||||
}
|
||||
|
||||
/// A volume that is compile-time sized and has its lower bound at `(0, 0, 0)`.
|
||||
@ -63,13 +55,9 @@ pub trait RasterableVol: BaseVol {
|
||||
}
|
||||
|
||||
impl<V: RasterableVol> SizedVol for V {
|
||||
fn lower_bound(&self) -> Vec3<i32> {
|
||||
Vec3::zero()
|
||||
}
|
||||
fn lower_bound(&self) -> Vec3<i32> { Vec3::zero() }
|
||||
|
||||
fn upper_bound(&self) -> Vec3<i32> {
|
||||
V::SIZE.map(|e| e as i32)
|
||||
}
|
||||
fn upper_bound(&self) -> Vec3<i32> { V::SIZE.map(|e| e as i32) }
|
||||
}
|
||||
|
||||
/// A volume whose cross section with the XY-plane is a rectangle.
|
||||
@ -92,13 +80,9 @@ pub trait RectRasterableVol: BaseVol {
|
||||
}
|
||||
|
||||
impl<V: RectRasterableVol> RectSizedVol for V {
|
||||
fn lower_bound_xy(&self) -> Vec2<i32> {
|
||||
Vec2::zero()
|
||||
}
|
||||
fn lower_bound_xy(&self) -> Vec2<i32> { Vec2::zero() }
|
||||
|
||||
fn upper_bound_xy(&self) -> Vec2<i32> {
|
||||
V::RECT_SIZE.map(|e| e as i32)
|
||||
}
|
||||
fn upper_bound_xy(&self) -> Vec2<i32> { V::RECT_SIZE.map(|e| e as i32) }
|
||||
}
|
||||
|
||||
/// A volume that provides read access to its voxel data.
|
||||
@ -118,24 +102,26 @@ pub trait ReadVol: BaseVol {
|
||||
}
|
||||
}
|
||||
|
||||
/// A volume that provides the ability to sample (i.e., clone a section of) its voxel data.
|
||||
/// A volume that provides the ability to sample (i.e., clone a section of) its
|
||||
/// voxel data.
|
||||
///
|
||||
/// TODO (haslersn): Do we still need this now that we have `IntoVolIterator`?
|
||||
pub trait SampleVol<I>: BaseVol {
|
||||
type Sample: BaseVol + ReadVol;
|
||||
/// Take a sample of the volume by cloning voxels within the provided range.
|
||||
///
|
||||
/// Note that value and accessibility of voxels outside the bounds of the sample is
|
||||
/// implementation-defined and should not be used.
|
||||
/// Note that value and accessibility of voxels outside the bounds of the
|
||||
/// sample is implementation-defined and should not be used.
|
||||
///
|
||||
/// Note that the resultant volume has a coordinate space relative to the sample, not the
|
||||
/// original volume.
|
||||
/// Note that the resultant volume has a coordinate space relative to the
|
||||
/// sample, not the original volume.
|
||||
fn sample(&self, range: I) -> Result<Self::Sample, Self::Error>;
|
||||
}
|
||||
|
||||
/// A volume that provides write access to its voxel data.
|
||||
pub trait WriteVol: BaseVol {
|
||||
/// Set the voxel at the provided position in the volume to the provided value.
|
||||
/// Set the voxel at the provided position in the volume to the provided
|
||||
/// value.
|
||||
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
@ -217,7 +203,8 @@ impl DefaultPosIterator {
|
||||
let end = if lower_bound.map2(upper_bound, |l, u| l < u).reduce_and() {
|
||||
upper_bound
|
||||
} else {
|
||||
// Special case because our implementation doesn't handle empty ranges for x or y:
|
||||
// Special case because our implementation doesn't handle empty ranges for x or
|
||||
// y:
|
||||
lower_bound
|
||||
};
|
||||
Self {
|
||||
|
@ -2,8 +2,7 @@ use crate::vol::{
|
||||
BaseVol, IntoPosIterator, IntoVolIterator, RasterableVol, ReadVol, VolSize, Vox, WriteVol,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::iter::Iterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::{iter::Iterator, marker::PhantomData};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -27,9 +26,9 @@ pub enum ChunkError {
|
||||
/// `self.indices : [u8; 256]`. It contains for each group
|
||||
///
|
||||
/// * (a) the order in which it has been inserted into `self.vox`, if the group
|
||||
/// is contained in `self.vox` or
|
||||
/// is contained in `self.vox` or
|
||||
/// * (b) 255, otherwise. That case represents that the whole group consists
|
||||
/// only of `self.default` voxels.
|
||||
/// only of `self.default` voxels.
|
||||
///
|
||||
/// (Note that 255 is a valid insertion order for case (a) only if `self.vox` is
|
||||
/// full and then no other group has the index 255. Therefore there's no
|
||||
@ -40,15 +39,16 @@ pub enum ChunkError {
|
||||
/// The index buffer should be small because:
|
||||
///
|
||||
/// * Small size increases the probability that it will always be in cache.
|
||||
/// * The index buffer is allocated for every `Chunk` and an almost empty `Chunk`
|
||||
/// shall not consume too much memory.
|
||||
/// * The index buffer is allocated for every `Chunk` and an almost empty
|
||||
/// `Chunk` shall not consume too much memory.
|
||||
///
|
||||
/// The number of 256 groups is particularly nice because it means that the index
|
||||
/// buffer can consist of `u8`s. This keeps the space requirement for the index
|
||||
/// buffer as low as 4 cache lines.
|
||||
/// The number of 256 groups is particularly nice because it means that the
|
||||
/// index buffer can consist of `u8`s. This keeps the space requirement for the
|
||||
/// index buffer as low as 4 cache lines.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Chunk<V: Vox, S: VolSize, M> {
|
||||
indices: Vec<u8>, // TODO (haslersn): Box<[u8; S::SIZE.x * S::SIZE.y * S::SIZE.z]>, this is however not possible in Rust yet
|
||||
indices: Vec<u8>, /* TODO (haslersn): Box<[u8; S::SIZE.x * S::SIZE.y * S::SIZE.z]>, this is
|
||||
* however not possible in Rust yet */
|
||||
vox: Vec<V>,
|
||||
default: V,
|
||||
meta: M,
|
||||
@ -56,8 +56,11 @@ pub struct Chunk<V: Vox, S: VolSize, M> {
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
const VOLUME: u32 = (S::SIZE.x * S::SIZE.y * S::SIZE.z) as u32;
|
||||
const GROUP_VOLUME: u32 = [Self::VOLUME / 256, 1][(Self::VOLUME < 256) as usize];
|
||||
const GROUP_COUNT: Vec3<u32> = Vec3::new(
|
||||
S::SIZE.x / Self::GROUP_SIZE.x,
|
||||
S::SIZE.y / Self::GROUP_SIZE.y,
|
||||
S::SIZE.z / Self::GROUP_SIZE.z,
|
||||
);
|
||||
/// `GROUP_COUNT_TOTAL` is always `256`, except if `VOLUME < 256`
|
||||
const GROUP_COUNT_TOTAL: u32 = Self::VOLUME / Self::GROUP_VOLUME;
|
||||
const GROUP_LONG_SIDE_LEN: u32 = 1 << ((Self::GROUP_VOLUME * 4 - 1).count_ones() / 3);
|
||||
@ -66,11 +69,8 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
Self::GROUP_LONG_SIDE_LEN,
|
||||
Self::GROUP_VOLUME / (Self::GROUP_LONG_SIDE_LEN * Self::GROUP_LONG_SIDE_LEN),
|
||||
);
|
||||
const GROUP_COUNT: Vec3<u32> = Vec3::new(
|
||||
S::SIZE.x / Self::GROUP_SIZE.x,
|
||||
S::SIZE.y / Self::GROUP_SIZE.y,
|
||||
S::SIZE.z / Self::GROUP_SIZE.z,
|
||||
);
|
||||
const GROUP_VOLUME: u32 = [Self::VOLUME / 256, 1][(Self::VOLUME < 256) as usize];
|
||||
const VOLUME: u32 = (S::SIZE.x * S::SIZE.y * S::SIZE.z) as u32;
|
||||
|
||||
/// Creates a new `Chunk` with the provided dimensions and all voxels filled
|
||||
/// with duplicates of the provided voxel.
|
||||
@ -116,14 +116,10 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
}
|
||||
|
||||
/// Get a reference to the internal metadata.
|
||||
pub fn metadata(&self) -> &M {
|
||||
&self.meta
|
||||
}
|
||||
pub fn metadata(&self) -> &M { &self.meta }
|
||||
|
||||
/// Get a mutable reference to the internal metadata.
|
||||
pub fn metadata_mut(&mut self) -> &mut M {
|
||||
&mut self.meta
|
||||
}
|
||||
pub fn metadata_mut(&mut self) -> &mut M { &mut self.meta }
|
||||
|
||||
#[inline(always)]
|
||||
fn grp_idx(pos: Vec3<i32>) -> u32 {
|
||||
@ -188,8 +184,8 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> BaseVol for Chunk<V, S, M> {
|
||||
type Vox = V;
|
||||
type Error = ChunkError;
|
||||
type Vox = V;
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> RasterableVol for Chunk<V, S, M> {
|
||||
|
@ -34,8 +34,8 @@ impl<V: Vox, M: Clone, A: Access> Clone for Dyna<V, M, A> {
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> Dyna<V, M, A> {
|
||||
/// Used to transform a voxel position in the volume into its corresponding index
|
||||
/// in the voxel array.
|
||||
/// Used to transform a voxel position in the volume into its corresponding
|
||||
/// index in the voxel array.
|
||||
#[inline(always)]
|
||||
fn idx_for(sz: Vec3<u32>, pos: Vec3<i32>) -> Option<usize> {
|
||||
if pos.map(|e| e >= 0).reduce_and() && pos.map2(sz, |e, lim| e < lim as i32).reduce_and() {
|
||||
@ -47,20 +47,16 @@ impl<V: Vox, M, A: Access> Dyna<V, M, A> {
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> BaseVol for Dyna<V, M, A> {
|
||||
type Vox = V;
|
||||
type Error = DynaError;
|
||||
type Vox = V;
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> SizedVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn lower_bound(&self) -> Vec3<i32> {
|
||||
Vec3::zero()
|
||||
}
|
||||
fn lower_bound(&self) -> Vec3<i32> { Vec3::zero() }
|
||||
|
||||
#[inline(always)]
|
||||
fn upper_bound(&self) -> Vec3<i32> {
|
||||
self.sz.map(|e| e as i32)
|
||||
}
|
||||
fn upper_bound(&self) -> Vec3<i32> { self.sz.map(|e| e as i32) }
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> ReadVol for Dyna<V, M, A> {
|
||||
@ -99,8 +95,8 @@ impl<'a, V: Vox, M, A: Access> IntoVolIterator<'a> for &'a Dyna<V, M, A> {
|
||||
}
|
||||
|
||||
impl<V: Vox + Clone, M, A: Access> Dyna<V, M, A> {
|
||||
/// Create a new `Dyna` with the provided dimensions and all voxels filled with duplicates of
|
||||
/// the provided voxel.
|
||||
/// Create a new `Dyna` with the provided dimensions and all voxels filled
|
||||
/// with duplicates of the provided voxel.
|
||||
pub fn filled(sz: Vec3<u32>, vox: V, meta: M) -> Self {
|
||||
Self {
|
||||
vox: vec![vox; sz.product() as usize],
|
||||
@ -111,14 +107,10 @@ impl<V: Vox + Clone, M, A: Access> Dyna<V, M, A> {
|
||||
}
|
||||
|
||||
/// Get a reference to the internal metadata.
|
||||
pub fn metadata(&self) -> &M {
|
||||
&self.meta
|
||||
}
|
||||
pub fn metadata(&self) -> &M { &self.meta }
|
||||
|
||||
/// Get a mutable reference to the internal metadata.
|
||||
pub fn metadata_mut(&mut self) -> &mut M {
|
||||
&mut self.meta
|
||||
}
|
||||
pub fn metadata_mut(&mut self) -> &mut M { &mut self.meta }
|
||||
}
|
||||
|
||||
pub trait Access {
|
||||
|
@ -37,8 +37,8 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
}
|
||||
|
||||
impl<V: RectRasterableVol + Debug> BaseVol for VolGrid2d<V> {
|
||||
type Vox = V::Vox;
|
||||
type Error = VolGrid2dError<V>;
|
||||
type Vox = V::Vox;
|
||||
}
|
||||
|
||||
impl<V: RectRasterableVol + ReadVol + Debug> ReadVol for VolGrid2d<V> {
|
||||
@ -55,14 +55,16 @@ impl<V: RectRasterableVol + ReadVol + Debug> ReadVol for VolGrid2d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This actually breaks the API: samples are supposed to have an offset of zero!
|
||||
// TODO: Should this be changed, perhaps?
|
||||
// TODO: This actually breaks the API: samples are supposed to have an offset of
|
||||
// zero! TODO: Should this be changed, perhaps?
|
||||
impl<I: Into<Aabr<i32>>, V: RectRasterableVol + ReadVol + Debug> SampleVol<I> for VolGrid2d<V> {
|
||||
type Sample = VolGrid2d<V>;
|
||||
|
||||
/// Take a sample of the terrain by cloning the voxels within the provided range.
|
||||
/// Take a sample of the terrain by cloning the voxels within the provided
|
||||
/// range.
|
||||
///
|
||||
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
||||
/// Note that the resultant volume does not carry forward metadata from the
|
||||
/// original chunks.
|
||||
fn sample(&self, range: I) -> Result<Self::Sample, VolGrid2dError<V>> {
|
||||
let range = range.into();
|
||||
|
||||
@ -115,9 +117,7 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk_size() -> Vec2<u32> {
|
||||
V::RECT_SIZE
|
||||
}
|
||||
pub fn chunk_size() -> Vec2<u32> { V::RECT_SIZE }
|
||||
|
||||
pub fn insert(&mut self, key: Vec2<i32>, chunk: Arc<V>) -> Option<Arc<V>> {
|
||||
self.chunks.insert(key, chunk)
|
||||
@ -130,29 +130,17 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_key_arc(&self, key: Vec2<i32>) -> Option<&Arc<V>> {
|
||||
self.chunks.get(&key)
|
||||
}
|
||||
pub fn get_key_arc(&self, key: Vec2<i32>) -> Option<&Arc<V>> { self.chunks.get(&key) }
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.chunks.clear();
|
||||
}
|
||||
pub fn clear(&mut self) { self.chunks.clear(); }
|
||||
|
||||
pub fn drain(&mut self) -> hash_map::Drain<Vec2<i32>, Arc<V>> {
|
||||
self.chunks.drain()
|
||||
}
|
||||
pub fn drain(&mut self) -> hash_map::Drain<Vec2<i32>, Arc<V>> { self.chunks.drain() }
|
||||
|
||||
pub fn remove(&mut self, key: Vec2<i32>) -> Option<Arc<V>> {
|
||||
self.chunks.remove(&key)
|
||||
}
|
||||
pub fn remove(&mut self, key: Vec2<i32>) -> Option<Arc<V>> { self.chunks.remove(&key) }
|
||||
|
||||
pub fn key_pos(&self, key: Vec2<i32>) -> Vec2<i32> {
|
||||
key * V::RECT_SIZE.map(|e| e as i32)
|
||||
}
|
||||
pub fn key_pos(&self, key: Vec2<i32>) -> Vec2<i32> { key * V::RECT_SIZE.map(|e| e as i32) }
|
||||
|
||||
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec2<i32> {
|
||||
Self::chunk_key(pos)
|
||||
}
|
||||
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec2<i32> { Self::chunk_key(pos) }
|
||||
|
||||
pub fn iter(&self) -> ChunkIter<V> {
|
||||
ChunkIter {
|
||||
@ -160,9 +148,7 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cached<'a>(&'a self) -> CachedVolGrid2d<'a, V> {
|
||||
CachedVolGrid2d::new(self)
|
||||
}
|
||||
pub fn cached<'a>(&'a self) -> CachedVolGrid2d<'a, V> { CachedVolGrid2d::new(self) }
|
||||
}
|
||||
|
||||
pub struct CachedVolGrid2d<'a, V: RectRasterableVol> {
|
||||
@ -213,9 +199,7 @@ impl<'a, V: RectRasterableVol + ReadVol> CachedVolGrid2d<'a, V> {
|
||||
impl<'a, V: RectRasterableVol> Deref for CachedVolGrid2d<'a, V> {
|
||||
type Target = VolGrid2d<V>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.vol_grid_2d
|
||||
}
|
||||
fn deref(&self) -> &Self::Target { self.vol_grid_2d }
|
||||
}
|
||||
|
||||
pub struct ChunkIter<'a, V: RectRasterableVol> {
|
||||
@ -225,7 +209,5 @@ pub struct ChunkIter<'a, V: RectRasterableVol> {
|
||||
impl<'a, V: RectRasterableVol> Iterator for ChunkIter<'a, V> {
|
||||
type Item = (Vec2<i32>, &'a Arc<V>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(k, c)| (*k, c))
|
||||
}
|
||||
fn next(&mut self) -> Option<Self::Item> { self.iter.next().map(|(k, c)| (*k, c)) }
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ impl<V: RasterableVol> VolGrid3d<V> {
|
||||
}
|
||||
|
||||
impl<V: RasterableVol + Debug> BaseVol for VolGrid3d<V> {
|
||||
type Vox = V::Vox;
|
||||
type Error = VolGrid3dError<V>;
|
||||
type Vox = V::Vox;
|
||||
}
|
||||
|
||||
impl<V: RasterableVol + ReadVol + Debug> ReadVol for VolGrid3d<V> {
|
||||
@ -53,14 +53,16 @@ impl<V: RasterableVol + ReadVol + Debug> ReadVol for VolGrid3d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This actually breaks the API: samples are supposed to have an offset of zero!
|
||||
// TODO: Should this be changed, perhaps?
|
||||
// TODO: This actually breaks the API: samples are supposed to have an offset of
|
||||
// zero! TODO: Should this be changed, perhaps?
|
||||
impl<I: Into<Aabb<i32>>, V: RasterableVol + ReadVol + Debug> SampleVol<I> for VolGrid3d<V> {
|
||||
type Sample = VolGrid3d<V>;
|
||||
|
||||
/// Take a sample of the terrain by cloning the voxels within the provided range.
|
||||
/// Take a sample of the terrain by cloning the voxels within the provided
|
||||
/// range.
|
||||
///
|
||||
/// Note that the resultant volume does not carry forward metadata from the original chunks.
|
||||
/// Note that the resultant volume does not carry forward metadata from the
|
||||
/// original chunks.
|
||||
fn sample(&self, range: I) -> Result<Self::Sample, VolGrid3dError<V>> {
|
||||
let range = range.into();
|
||||
let mut sample = VolGrid3d::new()?;
|
||||
@ -114,9 +116,7 @@ impl<V: RasterableVol> VolGrid3d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunk_size() -> Vec3<u32> {
|
||||
V::SIZE
|
||||
}
|
||||
pub fn chunk_size() -> Vec3<u32> { V::SIZE }
|
||||
|
||||
pub fn insert(&mut self, key: Vec3<i32>, chunk: Arc<V>) -> Option<Arc<V>> {
|
||||
self.chunks.insert(key, chunk)
|
||||
@ -129,21 +129,13 @@ impl<V: RasterableVol> VolGrid3d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_key_arc(&self, key: Vec3<i32>) -> Option<&Arc<V>> {
|
||||
self.chunks.get(&key)
|
||||
}
|
||||
pub fn get_key_arc(&self, key: Vec3<i32>) -> Option<&Arc<V>> { self.chunks.get(&key) }
|
||||
|
||||
pub fn remove(&mut self, key: Vec3<i32>) -> Option<Arc<V>> {
|
||||
self.chunks.remove(&key)
|
||||
}
|
||||
pub fn remove(&mut self, key: Vec3<i32>) -> Option<Arc<V>> { self.chunks.remove(&key) }
|
||||
|
||||
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> {
|
||||
key * V::SIZE.map(|e| e as i32)
|
||||
}
|
||||
pub fn key_pos(&self, key: Vec3<i32>) -> Vec3<i32> { key * V::SIZE.map(|e| e as i32) }
|
||||
|
||||
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec3<i32> {
|
||||
Self::chunk_key(pos)
|
||||
}
|
||||
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec3<i32> { Self::chunk_key(pos) }
|
||||
|
||||
pub fn iter(&self) -> ChunkIter<V> {
|
||||
ChunkIter {
|
||||
@ -159,7 +151,5 @@ pub struct ChunkIter<'a, V: RasterableVol> {
|
||||
impl<'a, V: RasterableVol> Iterator for ChunkIter<'a, V> {
|
||||
type Item = (Vec3<i32>, &'a Arc<V>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(k, c)| (*k, c))
|
||||
}
|
||||
fn next(&mut self) -> Option<Self::Item> { self.iter.next().map(|(k, c)| (*k, c)) }
|
||||
}
|
||||
|
@ -9,8 +9,7 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
use vek::*;
|
||||
#[cfg(feature = "worldgen")]
|
||||
use world::World;
|
||||
#[cfg(feature = "worldgen")] use world::World;
|
||||
|
||||
type ChunkGenResult = (
|
||||
Vec2<i32>,
|
||||
@ -31,6 +30,7 @@ impl ChunkGenerator {
|
||||
pending_chunks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_chunk(
|
||||
&mut self,
|
||||
entity: EcsEntity,
|
||||
@ -53,6 +53,7 @@ impl ChunkGenerator {
|
||||
let _ = chunk_tx.send((key, payload));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn recv_new_chunk(&mut self) -> Option<ChunkGenResult> {
|
||||
if let Ok((key, res)) = self.chunk_rx.try_recv() {
|
||||
self.pending_chunks.remove(&key);
|
||||
@ -62,9 +63,11 @@ impl ChunkGenerator {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_chunks<'a>(&'a self) -> impl Iterator<Item = Vec2<i32>> + 'a {
|
||||
self.pending_chunks.keys().copied()
|
||||
}
|
||||
|
||||
pub fn cancel_if_pending(&mut self, key: Vec2<i32>) {
|
||||
if let Some(cancel) = self.pending_chunks.remove(&key) {
|
||||
cancel.store(true, Ordering::Relaxed);
|
||||
|
@ -19,26 +19,28 @@ impl Component for Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn notify(&mut self, msg: ServerMsg) {
|
||||
self.postbox.send_message(msg);
|
||||
}
|
||||
pub fn notify(&mut self, msg: ServerMsg) { self.postbox.send_message(msg); }
|
||||
|
||||
pub fn is_registered(&self) -> bool {
|
||||
match self.client_state {
|
||||
ClientState::Registered | ClientState::Spectator | ClientState::Character => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ingame(&self) -> bool {
|
||||
match self.client_state {
|
||||
ClientState::Spectator | ClientState::Character => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allow_state(&mut self, new_state: ClientState) {
|
||||
self.client_state = new_state;
|
||||
self.postbox
|
||||
.send_message(ServerMsg::StateAnswer(Ok(new_state)));
|
||||
}
|
||||
|
||||
pub fn error_state(&mut self, error: RequestStateError) {
|
||||
self.postbox
|
||||
.send_message(ServerMsg::StateAnswer(Err((error, self.client_state))));
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! # Implementing new commands.
|
||||
//! To implement a new command, add an instance of `ChatCommand` to `CHAT_COMMANDS`
|
||||
//! and provide a handler function.
|
||||
//! To implement a new command, add an instance of `ChatCommand` to
|
||||
//! `CHAT_COMMANDS` and provide a handler function.
|
||||
|
||||
use crate::{Server, StateExt};
|
||||
use chrono::{NaiveTime, Timelike};
|
||||
@ -31,15 +31,19 @@ pub struct ChatCommand {
|
||||
arg_fmt: &'static str,
|
||||
/// A message that explains how the command is used.
|
||||
help_string: &'static str,
|
||||
/// A boolean that is used to check whether the command requires administrator permissions or not.
|
||||
/// A boolean that is used to check whether the command requires
|
||||
/// administrator permissions or not.
|
||||
needs_admin: bool,
|
||||
/// Handler function called when the command is executed.
|
||||
/// # Arguments
|
||||
/// * `&mut Server` - the `Server` instance executing the command.
|
||||
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the command.
|
||||
/// * `String` - a `String` containing the part of the command after the keyword.
|
||||
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the
|
||||
/// command.
|
||||
/// * `String` - a `String` containing the part of the command after the
|
||||
/// keyword.
|
||||
/// * `&ChatCommand` - the command to execute with the above arguments.
|
||||
/// Handler functions must parse arguments from the the given `String` (`scan_fmt!` is included for this purpose).
|
||||
/// Handler functions must parse arguments from the the given `String`
|
||||
/// (`scan_fmt!` is included for this purpose).
|
||||
handler: fn(&mut Server, EcsEntity, String, &ChatCommand),
|
||||
}
|
||||
|
||||
@ -60,7 +64,9 @@ impl ChatCommand {
|
||||
handler,
|
||||
}
|
||||
}
|
||||
/// Calls the contained handler function, passing `&self` as the last argument.
|
||||
|
||||
/// Calls the contained handler function, passing `&self` as the last
|
||||
/// argument.
|
||||
pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) {
|
||||
if self.needs_admin {
|
||||
if !server.entity_is_admin(entity) {
|
||||
@ -275,7 +281,7 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
||||
.state
|
||||
.write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z)));
|
||||
server.state.write_component(entity, comp::ForceUpdate);
|
||||
}
|
||||
},
|
||||
None => server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(String::from("You have no position.")),
|
||||
@ -335,7 +341,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
||||
ServerMsg::private(format!("'{}' is not a valid time.", n)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
None => {
|
||||
@ -352,7 +358,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
||||
};
|
||||
server.notify_client(entity, ServerMsg::private(msg));
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
server.state.ecs_mut().write_resource::<TimeOfDay>().0 =
|
||||
@ -429,7 +435,7 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
|
||||
Some(pos) => {
|
||||
server.state.write_component(entity, pos);
|
||||
server.state.write_component(entity, comp::ForceUpdate);
|
||||
}
|
||||
},
|
||||
None => server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)),
|
||||
@ -444,11 +450,11 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
|
||||
entity,
|
||||
ServerMsg::private(String::from(action.help_string)),
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
None => {
|
||||
server.notify_client(entity, ServerMsg::private(format!("You have no position!")));
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server.notify_client(entity, ServerMsg::private(String::from(action.help_string)));
|
||||
@ -509,17 +515,17 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
|
||||
entity,
|
||||
ServerMsg::private(format!("Spawned {} entities", amount).to_owned()),
|
||||
);
|
||||
}
|
||||
},
|
||||
None => server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private("You have no position!".to_owned()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
server.notify_client(entity, ServerMsg::private(String::from(action.help_string)));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -696,12 +702,13 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
|
||||
entity,
|
||||
ServerMsg::private(String::from("Object not found!")),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
server
|
||||
.create_object(pos, obj_type)
|
||||
.with(comp::Ori(
|
||||
// converts player orientation into a 90° rotation for the object by using the axis with the highest value
|
||||
// converts player orientation into a 90° rotation for the object by using the axis
|
||||
// with the highest value
|
||||
ori.0
|
||||
.map(|e| {
|
||||
if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() {
|
||||
@ -802,18 +809,15 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
|
||||
.state
|
||||
.ecs()
|
||||
.write_storage::<comp::LightEmitter>()
|
||||
.insert(
|
||||
entity,
|
||||
comp::LightEmitter {
|
||||
offset: Vec3::new(0.5, 0.2, 0.8),
|
||||
col: Rgb::new(1.0, 0.75, 0.3),
|
||||
strength: if let Some(s) = opt_s {
|
||||
s.max(0.0).min(10.0)
|
||||
} else {
|
||||
3.0
|
||||
},
|
||||
.insert(entity, comp::LightEmitter {
|
||||
offset: Vec3::new(0.5, 0.2, 0.8),
|
||||
col: Rgb::new(1.0, 0.75, 0.3),
|
||||
strength: if let Some(s) = opt_s {
|
||||
s.max(0.0).min(10.0)
|
||||
} else {
|
||||
3.0
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
server.notify_client(
|
||||
entity,
|
||||
@ -847,7 +851,7 @@ fn handle_waypoint(server: &mut Server, entity: EcsEntity, _args: String, _actio
|
||||
.write_storage::<comp::Waypoint>()
|
||||
.insert(entity, comp::Waypoint::new(pos.0));
|
||||
server.notify_client(entity, ServerMsg::private(String::from("Waypoint set!")));
|
||||
}
|
||||
},
|
||||
None => server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(String::from("You have no position!")),
|
||||
@ -866,10 +870,10 @@ fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action:
|
||||
Some(player) => match server.state.read_component_cloned::<comp::Admin>(player) {
|
||||
Some(_admin) => {
|
||||
ecs.write_storage::<comp::Admin>().remove(player);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
server.state.write_component(player, comp::Admin);
|
||||
}
|
||||
},
|
||||
},
|
||||
None => {
|
||||
server.notify_client(
|
||||
@ -877,7 +881,7 @@ fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action:
|
||||
ServerMsg::private(format!("Player '{}' not found!", alias)),
|
||||
);
|
||||
server.notify_client(entity, ServerMsg::private(String::from(action.help_string)));
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
server.notify_client(entity, ServerMsg::private(String::from(action.help_string)));
|
||||
@ -1040,10 +1044,10 @@ fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &Cha
|
||||
} else {
|
||||
error_msg = Some(ServerMsg::private(String::from("Player has no stats!")));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
error_msg = Some(ServerMsg::private(format!("Player '{}' not found!", alias)));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(msg) = error_msg {
|
||||
@ -1106,7 +1110,7 @@ fn handle_remove_lights(
|
||||
to_delete.push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(String::from("You have no position.")),
|
||||
|
@ -7,7 +7,5 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<PostError> for Error {
|
||||
fn from(err: PostError) -> Self {
|
||||
Error::Network(err)
|
||||
}
|
||||
fn from(err: PostError) -> Self { Error::Network(err) }
|
||||
}
|
||||
|
@ -3,7 +3,5 @@ pub struct Input {
|
||||
}
|
||||
|
||||
impl Default for Input {
|
||||
fn default() -> Self {
|
||||
Input {}
|
||||
}
|
||||
fn default() -> Self { Input {} }
|
||||
}
|
||||
|
@ -10,8 +10,7 @@ pub mod input;
|
||||
pub mod metrics;
|
||||
pub mod settings;
|
||||
pub mod sys;
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
mod test_world;
|
||||
#[cfg(not(feature = "worldgen"))] mod test_world;
|
||||
|
||||
// Reexports
|
||||
pub use crate::{error::Error, input::Input, settings::ServerSettings};
|
||||
@ -115,19 +114,16 @@ impl Server {
|
||||
state.ecs_mut().register::<Client>();
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
let world = World::generate(
|
||||
settings.world_seed,
|
||||
WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: if let Some(ref opts) = settings.map_file {
|
||||
opts.clone()
|
||||
} else {
|
||||
// Load default map from assets.
|
||||
FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into())
|
||||
},
|
||||
..WorldOpts::default()
|
||||
let world = World::generate(settings.world_seed, WorldOpts {
|
||||
seed_elements: true,
|
||||
world_file: if let Some(ref opts) = settings.map_file {
|
||||
opts.clone()
|
||||
} else {
|
||||
// Load default map from assets.
|
||||
FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into())
|
||||
},
|
||||
);
|
||||
..WorldOpts::default()
|
||||
});
|
||||
#[cfg(feature = "worldgen")]
|
||||
let map = world.sim().get_map();
|
||||
|
||||
@ -139,12 +135,14 @@ impl Server {
|
||||
#[cfg(feature = "worldgen")]
|
||||
let spawn_point = {
|
||||
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
||||
// but are needed to be explicit about casting (and to make the compiler stop complaining)
|
||||
// but are needed to be explicit about casting (and to make the compiler stop
|
||||
// complaining)
|
||||
|
||||
// spawn in the chunk, that is in the middle of the world
|
||||
let spawn_chunk: Vec2<i32> = WORLD_SIZE.map(|e| e as i32) / 2;
|
||||
// calculate the absolute position of the chunk in the world
|
||||
// (we could add TerrainChunkSize::RECT_SIZE / 2 here, to spawn in the midde of the chunk)
|
||||
// (we could add TerrainChunkSize::RECT_SIZE / 2 here, to spawn in the midde of
|
||||
// the chunk)
|
||||
let spawn_location = spawn_chunk * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
||||
|
||||
// get a z cache for the collumn in which we want to spawn
|
||||
@ -227,18 +225,13 @@ impl Server {
|
||||
}
|
||||
|
||||
/// Get a reference to the server's game state.
|
||||
pub fn state(&self) -> &State {
|
||||
&self.state
|
||||
}
|
||||
pub fn state(&self) -> &State { &self.state }
|
||||
|
||||
/// Get a mutable reference to the server's game state.
|
||||
pub fn state_mut(&mut self) -> &mut State {
|
||||
&mut self.state
|
||||
}
|
||||
pub fn state_mut(&mut self) -> &mut State { &mut self.state }
|
||||
|
||||
/// Get a reference to the server's world.
|
||||
pub fn world(&self) -> &World {
|
||||
&self.world
|
||||
}
|
||||
pub fn world(&self) -> &World { &self.world }
|
||||
|
||||
/// Build a static object entity
|
||||
pub fn create_object(
|
||||
@ -363,7 +356,7 @@ impl Server {
|
||||
.for_each(|pos| block_change.set(pos, Block::empty()))
|
||||
.cast();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Shoot {
|
||||
entity,
|
||||
@ -398,7 +391,7 @@ impl Server {
|
||||
}
|
||||
|
||||
builder.build();
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Damage { uid, change } => {
|
||||
let ecs = state.ecs();
|
||||
@ -407,7 +400,7 @@ impl Server {
|
||||
stats.health.change_by(change);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Destroy { entity, cause } => {
|
||||
// Chat message
|
||||
@ -440,7 +433,8 @@ impl Server {
|
||||
if let comp::HealthSource::Attack { by } = cause {
|
||||
state.ecs().entity_from_uid(by.into()).map(|attacker| {
|
||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
||||
// TODO: Discuss whether we should give EXP by Player
|
||||
// Killing or not.
|
||||
attacker_stats
|
||||
.exp
|
||||
.change_by((entity_stats.level.level() * 10) as i64);
|
||||
@ -487,7 +481,7 @@ impl Server {
|
||||
error!("Failed to delete destroyed entity: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::InventoryManip(entity, manip) => {
|
||||
match manip {
|
||||
@ -525,7 +519,7 @@ impl Server {
|
||||
}
|
||||
|
||||
state.write_component(entity, comp::InventoryUpdate);
|
||||
}
|
||||
},
|
||||
|
||||
comp::InventoryManip::Collect(pos) => {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
@ -543,7 +537,7 @@ impl Server {
|
||||
.map(|item| state.give_item(entity, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
comp::InventoryManip::Use(slot) => {
|
||||
let item_opt = state
|
||||
@ -571,10 +565,10 @@ impl Server {
|
||||
|
||||
stats.equipment.main = Some(item);
|
||||
}
|
||||
}
|
||||
},
|
||||
comp::ItemKind::Consumable { effect, .. } => {
|
||||
state.apply_effect(entity, effect);
|
||||
}
|
||||
},
|
||||
comp::ItemKind::Utility { kind } => match kind {
|
||||
comp::item::Utility::Collar => {
|
||||
let reinsert = if let Some(pos) =
|
||||
@ -640,7 +634,7 @@ impl Server {
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.insert(slot, item));
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
let _ = state
|
||||
@ -648,12 +642,12 @@ impl Server {
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.insert(slot, item));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
state.write_component(entity, comp::InventoryUpdate);
|
||||
}
|
||||
},
|
||||
|
||||
comp::InventoryManip::Swap(a, b) => {
|
||||
state
|
||||
@ -662,7 +656,7 @@ impl Server {
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.swap_slots(a, b));
|
||||
state.write_component(entity, comp::InventoryUpdate);
|
||||
}
|
||||
},
|
||||
|
||||
comp::InventoryManip::Drop(slot) => {
|
||||
let item = state
|
||||
@ -686,9 +680,9 @@ impl Server {
|
||||
));
|
||||
}
|
||||
state.write_component(entity, comp::InventoryUpdate);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Respawn(entity) => {
|
||||
// Only clients can respawn
|
||||
@ -717,11 +711,16 @@ impl Server {
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(entity, comp::ForceUpdate)
|
||||
.err().map(|err|
|
||||
error!("Error inserting ForceUpdate component when respawning client: {:?}", err)
|
||||
);
|
||||
.err()
|
||||
.map(|err| {
|
||||
error!(
|
||||
"Error inserting ForceUpdate component when respawning \
|
||||
client: {:?}",
|
||||
err
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::LandOnGround { entity, vel } => {
|
||||
if vel.z <= -37.0 {
|
||||
@ -737,7 +736,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Mount(mounter, mountee) => {
|
||||
if state
|
||||
@ -770,7 +769,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Unmount(mounter) => {
|
||||
let mountee_entity = state
|
||||
@ -786,7 +785,7 @@ impl Server {
|
||||
.map(|ms| *ms = comp::MountState::Unmounted);
|
||||
}
|
||||
state.delete_component::<comp::Mounting>(mounter);
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
||||
let ecs = state.ecs();
|
||||
@ -808,14 +807,11 @@ impl Server {
|
||||
"common.items.debug.possess",
|
||||
));
|
||||
} else {
|
||||
let _ = inventories.insert(
|
||||
possesse,
|
||||
comp::Inventory {
|
||||
slots: vec![Some(assets::load_expect_cloned(
|
||||
"common.items.debug.possess",
|
||||
))],
|
||||
},
|
||||
);
|
||||
let _ = inventories.insert(possesse, comp::Inventory {
|
||||
slots: vec![Some(assets::load_expect_cloned(
|
||||
"common.items.debug.possess",
|
||||
))],
|
||||
});
|
||||
}
|
||||
}
|
||||
let _ = ecs
|
||||
@ -847,7 +843,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::CreateCharacter {
|
||||
entity,
|
||||
@ -864,7 +860,7 @@ impl Server {
|
||||
&server_settings,
|
||||
);
|
||||
sys::subscription::initialize_region_subscription(state.ecs(), entity);
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::ExitIngame { entity } => {
|
||||
// Create new entity with just `Client`, `Uid`, and `Player` components
|
||||
@ -895,7 +891,7 @@ impl Server {
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete entity when removing character: {:?}", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::CreateNpc {
|
||||
pos,
|
||||
@ -911,7 +907,7 @@ impl Server {
|
||||
.with(scale)
|
||||
.with(alignment)
|
||||
.build();
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::CreateWaypoint(pos) => {
|
||||
self.create_object(comp::Pos(pos), comp::object::Body::CampfireLit)
|
||||
@ -922,7 +918,7 @@ impl Server {
|
||||
})
|
||||
.with(comp::WaypointArea::default())
|
||||
.build();
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::ClientDisconnect(entity) => {
|
||||
// Tell other clients to remove from player list
|
||||
@ -941,15 +937,15 @@ impl Server {
|
||||
}
|
||||
|
||||
frontend_events.push(Event::ClientDisconnected { entity });
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::ChunkRequest(entity, key) => {
|
||||
requested_chunks.push((entity, key));
|
||||
}
|
||||
},
|
||||
|
||||
ServerEvent::ChatCmd(entity, cmd) => {
|
||||
chat_commands.push((entity, cmd));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -977,23 +973,30 @@ impl Server {
|
||||
frontend_events
|
||||
}
|
||||
|
||||
/// Execute a single server tick, handle input and update the game state by the given duration.
|
||||
/// Execute a single server tick, handle input and update the game state by
|
||||
/// the given duration.
|
||||
pub fn tick(&mut self, _input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||
self.state.ecs().write_resource::<Tick>().0 += 1;
|
||||
// This tick function is the centre of the Veloren universe. Most server-side things are
|
||||
// managed from here, and as such it's important that it stays organised. Please consult
|
||||
// the core developers before making significant changes to this code. Here is the
|
||||
// approximate order of things. Please update it as this code changes.
|
||||
// This tick function is the centre of the Veloren universe. Most server-side
|
||||
// things are managed from here, and as such it's important that it
|
||||
// stays organised. Please consult the core developers before making
|
||||
// significant changes to this code. Here is the approximate order of
|
||||
// things. Please update it as this code changes.
|
||||
//
|
||||
// 1) Collect input from the frontend, apply input effects to the state of the game
|
||||
// 2) Go through any events (timer-driven or otherwise) that need handling and apply them
|
||||
// to the state of the game
|
||||
// 3) Go through all incoming client network communications, apply them to the game state
|
||||
// 4) Perform a single LocalState tick (i.e: update the world and entities in the world)
|
||||
// 5) Go through the terrain update queue and apply all changes to the terrain
|
||||
// 1) Collect input from the frontend, apply input effects to the
|
||||
// state of the game
|
||||
// 2) Go through any events (timer-driven or otherwise) that need handling
|
||||
// and apply them to the state of the game
|
||||
// 3) Go through all incoming client network communications, apply them to
|
||||
// the game state
|
||||
// 4) Perform a single LocalState tick (i.e: update the world and entities
|
||||
// in the world)
|
||||
// 5) Go through the terrain update queue and apply all changes to
|
||||
// the terrain
|
||||
// 6) Send relevant state updates to all clients
|
||||
// 7) Update Metrics with current data
|
||||
// 8) Finish the tick, passing control of the main thread back to the frontend
|
||||
// 8) Finish the tick, passing control of the main thread back
|
||||
// to the frontend
|
||||
|
||||
let before_tick_1 = Instant::now();
|
||||
// 1) Build up a list of events for this frame, to be passed to the frontend.
|
||||
@ -1009,7 +1012,8 @@ impl Server {
|
||||
// 3) Handle inputs from clients
|
||||
frontend_events.append(&mut self.handle_new_connections()?);
|
||||
|
||||
// Run message recieving sys before the systems in common for decreased latency (e.g. run before controller system)
|
||||
// Run message recieving sys before the systems in common for decreased latency
|
||||
// (e.g. run before controller system)
|
||||
sys::message::Sys.run_now(&self.state.ecs());
|
||||
|
||||
let before_tick_4 = Instant::now();
|
||||
@ -1171,8 +1175,8 @@ impl Server {
|
||||
.create_entity_synced()
|
||||
.with(client)
|
||||
.build();
|
||||
// Send client all the tracked components currently attached to its entity as well
|
||||
// as synced resources (currently only `TimeOfDay`)
|
||||
// Send client all the tracked components currently attached to its entity as
|
||||
// well as synced resources (currently only `TimeOfDay`)
|
||||
log::debug!("Starting initial sync with client.");
|
||||
self.state
|
||||
.ecs()
|
||||
@ -1229,7 +1233,7 @@ impl Server {
|
||||
kwd
|
||||
)));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1242,9 +1246,7 @@ impl Server {
|
||||
}
|
||||
|
||||
impl Drop for Server {
|
||||
fn drop(&mut self) {
|
||||
self.state.notify_registered_clients(ServerMsg::Shutdown);
|
||||
}
|
||||
fn drop(&mut self) { self.state.notify_registered_clients(ServerMsg::Shutdown); }
|
||||
}
|
||||
|
||||
trait StateExt {
|
||||
@ -1284,13 +1286,13 @@ impl StateExt for State {
|
||||
.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|stats| stats.health.change_by(change));
|
||||
}
|
||||
},
|
||||
Effect::Xp(xp) => {
|
||||
self.ecs()
|
||||
.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|stats| stats.exp.change_by(xp));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1347,7 +1349,11 @@ impl StateExt for State {
|
||||
// Don't panic if the entity wasn't found in a region maybe it was just created
|
||||
// and then deleted before the region manager had a chance to assign it a
|
||||
// region
|
||||
warn!("Failed to find region containing entity during entity deletion, assuming it wasn't sent to any clients and so deletion doesn't need to be recorded for sync purposes");
|
||||
warn!(
|
||||
"Failed to find region containing entity during entity deletion, assuming \
|
||||
it wasn't sent to any clients and so deletion doesn't need to be \
|
||||
recorded for sync purposes"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use log::info;
|
||||
use prometheus::{Encoder, Gauge, IntGauge, IntGaugeVec, Opts, Registry, TextEncoder};
|
||||
use rouille::{router, Server};
|
||||
use std::error::Error;
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
error::Error,
|
||||
net::SocketAddr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
|
@ -18,8 +18,8 @@ pub struct ServerSettings {
|
||||
//pub login_server: whatever
|
||||
pub start_time: f64,
|
||||
pub admins: Vec<String>,
|
||||
/// When set to None, loads the default map file (if available); otherwise, uses the value of
|
||||
/// the file options to decide how to proceed.
|
||||
/// When set to None, loads the default map file (if available); otherwise,
|
||||
/// uses the value of the file options to decide how to proceed.
|
||||
pub map_file: Option<FileOpts>,
|
||||
}
|
||||
|
||||
@ -69,14 +69,14 @@ impl ServerSettings {
|
||||
Err(e) => {
|
||||
log::warn!("Failed to parse setting file! Fallback to default. {}", e);
|
||||
Self::default()
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
let default_settings = Self::default();
|
||||
|
||||
match default_settings.save_to_file() {
|
||||
Err(e) => log::error!("Failed to create default setting file! {}", e),
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
default_settings
|
||||
}
|
||||
@ -97,7 +97,8 @@ impl ServerSettings {
|
||||
pub fn singleplayer() -> Self {
|
||||
let load = Self::load();
|
||||
Self {
|
||||
//BUG: theoretically another process can grab the port between here and server creation, however the timewindow is quite small
|
||||
//BUG: theoretically another process can grab the port between here and server
|
||||
// creation, however the timewindow is quite small
|
||||
gameserver_address: SocketAddr::from((
|
||||
[127, 0, 0, 1],
|
||||
pick_unused_port().expect("Failed to find unused port!"),
|
||||
@ -116,12 +117,11 @@ impl ServerSettings {
|
||||
server_description: "Who needs friends anyway?".to_owned(),
|
||||
max_players: 100,
|
||||
start_time: 9.0 * 3600.0,
|
||||
admins: vec!["singleplayer".to_string()], // TODO: Let the player choose if they want to use admin commands or not
|
||||
..load // Fill in remaining fields from settings.ron.
|
||||
admins: vec!["singleplayer".to_string()], /* TODO: Let the player choose if they want
|
||||
* to use admin commands or not */
|
||||
..load // Fill in remaining fields from settings.ron.
|
||||
}
|
||||
}
|
||||
|
||||
fn get_settings_path() -> PathBuf {
|
||||
PathBuf::from(r"settings.ron")
|
||||
}
|
||||
fn get_settings_path() -> PathBuf { PathBuf::from(r"settings.ron") }
|
||||
}
|
||||
|
@ -78,10 +78,16 @@ impl<'a> System<'a> for Sys {
|
||||
// To send entity updates
|
||||
// 1. Iterate through regions
|
||||
// 2. Iterate through region subscribers (ie clients)
|
||||
// - Collect a list of entity ids for clients who are subscribed to this region (hash calc to check each)
|
||||
// - Collect a list of entity ids for clients who are subscribed to this
|
||||
// region (hash calc to check each)
|
||||
// 3. Iterate through events from that region
|
||||
// - For each entity entered event, iterate through the client list and check if they are subscribed to the source (hash calc per subscribed client per entity event), if not subscribed to the source send a entity creation message to that client
|
||||
// - For each entity left event, iterate through the client list and check if they are subscribed to the destination (hash calc per subscribed client per entity event)
|
||||
// - For each entity entered event, iterate through the client list and
|
||||
// check if they are subscribed to the source (hash calc per subscribed
|
||||
// client per entity event), if not subscribed to the source send a entity
|
||||
// creation message to that client
|
||||
// - For each entity left event, iterate through the client list and check
|
||||
// if they are subscribed to the destination (hash calc per subscribed
|
||||
// client per entity event)
|
||||
// 4. Iterate through entities in that region
|
||||
// 5. Inform clients of the component changes for that entity
|
||||
// - Throttle update rate base on distance to each client
|
||||
@ -89,8 +95,8 @@ impl<'a> System<'a> for Sys {
|
||||
// Sync physics
|
||||
// via iterating through regions
|
||||
for (key, region) in region_map.iter() {
|
||||
// Assemble subscriber list for this region by iterating through clients and checking
|
||||
// if they are subscribed to this region
|
||||
// Assemble subscriber list for this region by iterating through clients and
|
||||
// checking if they are subscribed to this region
|
||||
let mut subscribers = (&mut clients, &entities, &subscriptions, &positions)
|
||||
.join()
|
||||
.filter_map(|(client, entity, subscription, pos)| {
|
||||
@ -146,7 +152,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
RegionEvent::Left(id, maybe_key) => {
|
||||
// Lookup UID for entity
|
||||
if let Some(&uid) = uids.get(entities.entity(*id)) {
|
||||
@ -160,7 +166,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,8 +235,8 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// TODO: An entity that stoppped moving on a tick that it wasn't sent to the player
|
||||
// will never have it's position updated
|
||||
// TODO: An entity that stoppped moving on a tick that it wasn't sent to the
|
||||
// player will never have it's position updated
|
||||
if last_pos.get(entity).map(|&l| l.0 != pos).unwrap_or(true) {
|
||||
let _ = last_pos.insert(entity, Last(pos));
|
||||
send_msg(
|
||||
@ -299,7 +305,8 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle entity deletion in regions that don't exist in RegionMap (theoretically none)
|
||||
// Handle entity deletion in regions that don't exist in RegionMap
|
||||
// (theoretically none)
|
||||
for (region_key, deleted) in deleted_entities.take_remaining_deleted() {
|
||||
for client in
|
||||
(&mut clients, &subscriptions)
|
||||
@ -330,7 +337,8 @@ impl<'a> System<'a> for Sys {
|
||||
inventory_updates.clear();
|
||||
|
||||
// Sync resources
|
||||
// TODO: doesn't really belong in this system (rename system or create another system?)
|
||||
// TODO: doesn't really belong in this system (rename system or create another
|
||||
// system?)
|
||||
let tof_msg = ServerMsg::TimeOfDay(*time_of_day);
|
||||
for client in (&mut clients).join() {
|
||||
client.notify(tof_msg.clone());
|
||||
|
@ -105,12 +105,12 @@ impl<'a> System<'a> for Sys {
|
||||
// Use ClientMsg::Register instead.
|
||||
ClientState::Connected => {
|
||||
client.error_state(RequestStateError::WrongMessage)
|
||||
}
|
||||
},
|
||||
ClientState::Registered => client.error_state(RequestStateError::Already),
|
||||
ClientState::Spectator | ClientState::Character => {
|
||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
// Request spectator state
|
||||
ClientMsg::Spectate => match client.client_state {
|
||||
@ -119,8 +119,8 @@ impl<'a> System<'a> for Sys {
|
||||
ClientState::Spectator => client.error_state(RequestStateError::Already),
|
||||
ClientState::Registered | ClientState::Character => {
|
||||
client.allow_state(ClientState::Spectator)
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
// Valid player
|
||||
ClientMsg::Register { player, password } if player.is_valid() => {
|
||||
@ -142,12 +142,12 @@ impl<'a> System<'a> for Sys {
|
||||
)));
|
||||
// Add to list to notify all clients of the new player
|
||||
new_players.push(entity);
|
||||
}
|
||||
},
|
||||
// Use RequestState instead (No need to send `player` again).
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
}
|
||||
//client.allow_state(ClientState::Registered);
|
||||
}
|
||||
},
|
||||
// Invalid player
|
||||
ClientMsg::Register { .. } => client.error_state(RequestStateError::Impossible),
|
||||
ClientMsg::SetViewDistance(view_distance) => match client.client_state {
|
||||
@ -155,8 +155,8 @@ impl<'a> System<'a> for Sys {
|
||||
players
|
||||
.get_mut(entity)
|
||||
.map(|player| player.view_distance = Some(view_distance));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
ClientMsg::Character { name, body, main } => match client.client_state {
|
||||
// Become Registered first.
|
||||
@ -184,35 +184,35 @@ impl<'a> System<'a> for Sys {
|
||||
body,
|
||||
main,
|
||||
});
|
||||
}
|
||||
},
|
||||
ClientState::Character => client.error_state(RequestStateError::Already),
|
||||
ClientState::Pending => {}
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ControllerInputs(inputs) => match client.client_state {
|
||||
ClientState::Connected
|
||||
| ClientState::Registered
|
||||
| ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
}
|
||||
},
|
||||
ClientState::Character => {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.inputs = inputs;
|
||||
}
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ControlEvent(event) => match client.client_state {
|
||||
ClientState::Connected
|
||||
| ClientState::Registered
|
||||
| ClientState::Spectator => {
|
||||
client.error_state(RequestStateError::Impossible)
|
||||
}
|
||||
},
|
||||
ClientState::Character => {
|
||||
if let Some(controller) = controllers.get_mut(entity) {
|
||||
controller.events.push(event);
|
||||
}
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::ChatMsg { message } => match client.client_state {
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
@ -226,7 +226,7 @@ impl<'a> System<'a> for Sys {
|
||||
message.len()
|
||||
),
|
||||
},
|
||||
ClientState::Pending => {}
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
|
||||
ClientState::Character => {
|
||||
@ -237,7 +237,7 @@ impl<'a> System<'a> for Sys {
|
||||
let _ = velocities.insert(entity, vel);
|
||||
let _ = orientations.insert(entity, ori);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Only characters can send positions.
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
},
|
||||
@ -245,16 +245,16 @@ impl<'a> System<'a> for Sys {
|
||||
if can_build.get(entity).is_some() {
|
||||
block_changes.set(pos, Block::empty());
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientMsg::PlaceBlock(pos, block) => {
|
||||
if can_build.get(entity).is_some() {
|
||||
block_changes.try_set(pos, block);
|
||||
}
|
||||
}
|
||||
},
|
||||
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
||||
ClientState::Connected | ClientState::Registered => {
|
||||
client.error_state(RequestStateError::Impossible);
|
||||
}
|
||||
},
|
||||
ClientState::Spectator | ClientState::Character => {
|
||||
match terrain.get_key(key) {
|
||||
Some(chunk) => {
|
||||
@ -262,18 +262,18 @@ impl<'a> System<'a> for Sys {
|
||||
key,
|
||||
chunk: Ok(Box::new(chunk.clone())),
|
||||
})
|
||||
}
|
||||
},
|
||||
None => server_emitter.emit(ServerEvent::ChunkRequest(entity, key)),
|
||||
}
|
||||
}
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientState::Pending => {},
|
||||
},
|
||||
// Always possible.
|
||||
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
||||
ClientMsg::Pong => {}
|
||||
ClientMsg::Pong => {},
|
||||
ClientMsg::Disconnect => {
|
||||
disconnect = true;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,7 +324,7 @@ impl<'a> System<'a> for Sys {
|
||||
} else {
|
||||
format!("[{}] {}", &player.alias, message)
|
||||
}
|
||||
}
|
||||
},
|
||||
None => format!("[<Unknown>] {}", message),
|
||||
};
|
||||
let msg = ServerMsg::ChatMsg { chat_type, message };
|
||||
@ -338,10 +338,10 @@ impl<'a> System<'a> for Sys {
|
||||
client.notify(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("Invalid message type.");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,16 +27,13 @@ const WAYPOINT_SYS: &str = "waypoint_sys";
|
||||
pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
// TODO: makes some of these dependent on systems in common like the phys system
|
||||
dispatch_builder.add(sentinel::Sys, SENTINEL_SYS, &[common::sys::PHYS_SYS]);
|
||||
dispatch_builder.add(
|
||||
subscription::Sys,
|
||||
dispatch_builder.add(subscription::Sys, SUBSCRIPTION_SYS, &[
|
||||
common::sys::PHYS_SYS,
|
||||
]);
|
||||
dispatch_builder.add(entity_sync::Sys, ENTITY_SYNC_SYS, &[
|
||||
SUBSCRIPTION_SYS,
|
||||
&[common::sys::PHYS_SYS],
|
||||
);
|
||||
dispatch_builder.add(
|
||||
entity_sync::Sys,
|
||||
ENTITY_SYNC_SYS,
|
||||
&[SUBSCRIPTION_SYS, SENTINEL_SYS],
|
||||
);
|
||||
SENTINEL_SYS,
|
||||
]);
|
||||
dispatch_builder.add(terrain_sync::Sys, TERRAIN_SYS, &[]);
|
||||
dispatch_builder.add(terrain::Sys, TERRAIN_SYNC_SYS, &[TERRAIN_SYS]);
|
||||
dispatch_builder.add(waypoint::Sys, WAYPOINT_SYS, &[]);
|
||||
@ -55,6 +52,7 @@ impl<S> SysTimer<S> {
|
||||
}
|
||||
self.start = Some(Instant::now());
|
||||
}
|
||||
|
||||
pub fn end(&mut self) {
|
||||
self.nanos = self
|
||||
.start
|
||||
|
@ -15,7 +15,8 @@ use specs::{
|
||||
use vek::*;
|
||||
|
||||
/// Always watching
|
||||
/// This system will monitor specific components for insertion, removal, and modification
|
||||
/// This system will monitor specific components for insertion, removal, and
|
||||
/// modification
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
@ -226,12 +227,15 @@ impl DeletedEntities {
|
||||
.or_insert(Vec::new())
|
||||
.push(uid.into());
|
||||
}
|
||||
|
||||
pub fn take_deleted_in_region(&mut self, key: Vec2<i32>) -> Option<Vec<u64>> {
|
||||
self.map.remove(&key)
|
||||
}
|
||||
|
||||
pub fn get_deleted_in_region(&mut self, key: Vec2<i32>) -> Option<&Vec<u64>> {
|
||||
self.map.get(&key)
|
||||
}
|
||||
|
||||
pub fn take_remaining_deleted(&mut self) -> Vec<(Vec2<i32>, Vec<u64>)> {
|
||||
// TODO: don't allocate
|
||||
self.map.drain().collect()
|
||||
|
@ -89,7 +89,8 @@ impl<'a> System<'a> for Sys {
|
||||
let chunk = (Vec2::<f32>::from(pos.0))
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32);
|
||||
// Only update regions when moving to a new chunk
|
||||
// uses a fuzzy border to prevent rapid triggering when moving along chunk boundaries
|
||||
// uses a fuzzy border to prevent rapid triggering when moving along chunk
|
||||
// boundaries
|
||||
if chunk != subscription.fuzzy_chunk
|
||||
&& (subscription
|
||||
.fuzzy_chunk
|
||||
@ -126,13 +127,18 @@ impl<'a> System<'a> for Sys {
|
||||
for key in regions_to_remove.drain(..) {
|
||||
// Remove region from this client's set of subscribed regions
|
||||
subscription.regions.remove(&key);
|
||||
// Tell the client to delete the entities in that region if it exists in the RegionMap
|
||||
// Tell the client to delete the entities in that region if it exists in the
|
||||
// RegionMap
|
||||
if let Some(region) = region_map.get(key) {
|
||||
// Process entity left events since they won't be processed during entity sync because this region is no longer subscribed to
|
||||
// Process entity left events since they won't be processed during entity
|
||||
// sync because this region is no longer subscribed to
|
||||
// TODO: consider changing system ordering??
|
||||
for event in region.events() {
|
||||
match event {
|
||||
RegionEvent::Entered(_, _) => {} // These don't need to be processed because this region is being thrown out anyway
|
||||
RegionEvent::Entered(_, _) => {}, /* These don't need to be */
|
||||
// processed because this
|
||||
// region is being thrown out
|
||||
// anyway
|
||||
RegionEvent::Left(id, maybe_key) => {
|
||||
// Lookup UID for entity
|
||||
// Doesn't overlap with entity deletion in sync packages
|
||||
@ -148,7 +154,7 @@ impl<'a> System<'a> for Sys {
|
||||
client.notify(ServerMsg::DeleteEntity(uid.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
// Tell client to delete entities in the region
|
||||
@ -156,7 +162,8 @@ impl<'a> System<'a> for Sys {
|
||||
client.notify(ServerMsg::DeleteEntity(uid.into()));
|
||||
}
|
||||
}
|
||||
// Send deleted entities since they won't be processed for this client in entity sync
|
||||
// Send deleted entities since they won't be processed for this client in entity
|
||||
// sync
|
||||
for uid in deleted_entities
|
||||
.get_deleted_in_region(key)
|
||||
.iter()
|
||||
@ -263,16 +270,16 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) {
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = world.write_storage().insert(
|
||||
entity,
|
||||
RegionSubscription {
|
||||
fuzzy_chunk,
|
||||
regions,
|
||||
},
|
||||
) {
|
||||
if let Err(err) = world.write_storage().insert(entity, RegionSubscription {
|
||||
fuzzy_chunk,
|
||||
regions,
|
||||
}) {
|
||||
error!("Failed to insert region subscription component: {:?}", err);
|
||||
}
|
||||
} else {
|
||||
debug!("Failed to initialize region subcription. Couldn't retrieve all the neccesary components on the provided entity");
|
||||
debug!(
|
||||
"Failed to initialize region subcription. Couldn't retrieve all the neccesary \
|
||||
components on the provided entity"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage};
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
/// This system will handle loading generated chunks and unloading uneeded chunks.
|
||||
/// This system will handle loading generated chunks and unloading
|
||||
/// uneeded chunks.
|
||||
/// 1. Inserts newly generated chunks into the TerrainGrid
|
||||
/// 2. Sends new chunks to neaby clients
|
||||
/// 3. Handles the chunk's supplement (e.g. npcs)
|
||||
@ -63,7 +64,7 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
}
|
||||
continue 'insert_terrain_chunks;
|
||||
}
|
||||
},
|
||||
};
|
||||
// Send the chunk to all nearby players.
|
||||
for (view_distance, pos, client) in (&players, &positions, &mut clients)
|
||||
@ -182,7 +183,8 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let mut scale = 1.0;
|
||||
|
||||
// TODO: Remove this and implement scaling or level depending on stuff like species instead
|
||||
// TODO: Remove this and implement scaling or level depending on stuff like
|
||||
// species instead
|
||||
stats.level.set_level(rand::thread_rng().gen_range(1, 4));
|
||||
|
||||
if let EntityKind::Boss = entity.kind {
|
||||
|
@ -8,7 +8,8 @@ use common::{
|
||||
};
|
||||
use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
||||
|
||||
/// This system will handle loading generated chunks and unloading uneeded chunks.
|
||||
/// This system will handle loading generated chunks and unloading
|
||||
/// uneeded chunks.
|
||||
/// 1. Inserts newly generated chunks into the TerrainGrid
|
||||
/// 2. Sends new chunks to neaby clients
|
||||
/// 3. Handles the chunk's supplement (e.g. npcs)
|
||||
|
@ -12,9 +12,7 @@ pub const WORLD_SIZE: Vec2<usize> = Vec2 { x: 1, y: 1 };
|
||||
pub struct World;
|
||||
|
||||
impl World {
|
||||
pub fn generate(_seed: u32) -> Self {
|
||||
Self
|
||||
}
|
||||
pub fn generate(_seed: u32) -> Self { Self }
|
||||
|
||||
pub fn tick(&self, dt: Duration) {}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use common::terrain::Block;
|
||||
use common::terrain::TerrainGrid;
|
||||
use common::vol::SampleVol;
|
||||
use common::vol::Vox;
|
||||
use common::{
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::{SampleVol, Vox},
|
||||
};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
@ -14,17 +14,14 @@ const GEN_SIZE: i32 = 4;
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
// Generate chunks here to test
|
||||
let mut terrain = TerrainGrid::new().unwrap();
|
||||
let world = World::generate(
|
||||
42,
|
||||
sim::WorldOpts {
|
||||
// NOTE: If this gets too expensive, we can turn it off.
|
||||
// TODO: Consider an option to turn off all erosion as well, or even provide altitude
|
||||
// directly with a closure.
|
||||
seed_elements: true,
|
||||
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let world = World::generate(42, sim::WorldOpts {
|
||||
// NOTE: If this gets too expensive, we can turn it off.
|
||||
// TODO: Consider an option to turn off all erosion as well, or even provide altitude
|
||||
// directly with a closure.
|
||||
seed_elements: true,
|
||||
world_file: sim::FileOpts::LoadAsset(sim::DEFAULT_WORLD_MAP.into()),
|
||||
..Default::default()
|
||||
});
|
||||
(0..GEN_SIZE)
|
||||
.flat_map(|x| (0..GEN_SIZE).map(move |y| Vec2::new(x, y)))
|
||||
.map(|offset| offset + CENTER)
|
||||
@ -35,16 +32,17 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
let sample = |chunk_pos: Vec2<i32>| {
|
||||
let chunk_pos = chunk_pos + CENTER;
|
||||
// Find the area of the terrain we want. Because meshing needs to compute things like
|
||||
// ambient occlusion and edge elision, we also need the borders of the chunk's
|
||||
// neighbours too (hence the `- 1` and `+ 1`).
|
||||
// Find the area of the terrain we want. Because meshing needs to compute things
|
||||
// like ambient occlusion and edge elision, we also need the borders of
|
||||
// the chunk's neighbours too (hence the `- 1` and `+ 1`).
|
||||
let aabr = Aabr {
|
||||
min: chunk_pos.map2(TerrainGrid::chunk_size(), |e, sz| e * sz as i32 - 1),
|
||||
max: chunk_pos.map2(TerrainGrid::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1),
|
||||
};
|
||||
|
||||
// Copy out the chunk data we need to perform the meshing. We do this by taking a
|
||||
// sample of the terrain that includes both the chunk we want and its neighbours.
|
||||
// Copy out the chunk data we need to perform the meshing. We do this by taking
|
||||
// a sample of the terrain that includes both the chunk we want and its
|
||||
// neighbours.
|
||||
let volume = terrain.sample(aabr).unwrap();
|
||||
|
||||
// The region to actually mesh
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct IdleAnimation;
|
||||
|
||||
impl Animation for IdleAnimation {
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
type Dependency = f64;
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct JumpAnimation;
|
||||
|
||||
impl Animation for JumpAnimation {
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -3,9 +3,7 @@ pub mod jump;
|
||||
pub mod run;
|
||||
|
||||
// Reexports
|
||||
pub use self::idle::IdleAnimation;
|
||||
pub use self::jump::JumpAnimation;
|
||||
pub use self::run::RunAnimation;
|
||||
pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation};
|
||||
|
||||
use super::{Bone, Skeleton};
|
||||
use crate::render::FigureBoneData;
|
||||
@ -48,6 +46,7 @@ impl BipedLargeSkeleton {
|
||||
|
||||
impl Skeleton for BipedLargeSkeleton {
|
||||
type Attr = SkeletonAttr;
|
||||
|
||||
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
||||
let upper_torso_mat = self.upper_torso.compute_base_matrix();
|
||||
let shoulder_l_mat = self.shoulder_l.compute_base_matrix();
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct RunAnimation;
|
||||
|
||||
impl Animation for RunAnimation {
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct IdleAnimation;
|
||||
|
||||
impl Animation for IdleAnimation {
|
||||
type Skeleton = BirdMediumSkeleton;
|
||||
type Dependency = f64;
|
||||
type Skeleton = BirdMediumSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct JumpAnimation;
|
||||
|
||||
impl Animation for JumpAnimation {
|
||||
type Skeleton = BirdMediumSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
type Skeleton = BirdMediumSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -3,9 +3,7 @@ pub mod jump;
|
||||
pub mod run;
|
||||
|
||||
// Reexports
|
||||
pub use self::idle::IdleAnimation;
|
||||
pub use self::jump::JumpAnimation;
|
||||
pub use self::run::RunAnimation;
|
||||
pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation};
|
||||
|
||||
use super::{Bone, Skeleton};
|
||||
use crate::render::FigureBoneData;
|
||||
@ -23,13 +21,12 @@ pub struct BirdMediumSkeleton {
|
||||
}
|
||||
|
||||
impl BirdMediumSkeleton {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
impl Skeleton for BirdMediumSkeleton {
|
||||
type Attr = SkeletonAttr;
|
||||
|
||||
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
||||
let torso_mat = self.torso.compute_base_matrix();
|
||||
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct RunAnimation;
|
||||
|
||||
impl Animation for RunAnimation {
|
||||
type Skeleton = BirdMediumSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
type Skeleton = BirdMediumSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct IdleAnimation;
|
||||
|
||||
impl Animation for IdleAnimation {
|
||||
type Skeleton = BirdSmallSkeleton;
|
||||
type Dependency = f64;
|
||||
type Skeleton = BirdSmallSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct JumpAnimation;
|
||||
|
||||
impl Animation for JumpAnimation {
|
||||
type Skeleton = BirdSmallSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
type Skeleton = BirdSmallSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -3,9 +3,7 @@ pub mod jump;
|
||||
pub mod run;
|
||||
|
||||
// Reexports
|
||||
pub use self::idle::IdleAnimation;
|
||||
pub use self::jump::JumpAnimation;
|
||||
pub use self::run::RunAnimation;
|
||||
pub use self::{idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation};
|
||||
|
||||
use super::{Bone, Skeleton};
|
||||
use crate::render::FigureBoneData;
|
||||
@ -32,6 +30,7 @@ impl BirdSmallSkeleton {
|
||||
|
||||
impl Skeleton for BirdSmallSkeleton {
|
||||
type Attr = SkeletonAttr;
|
||||
|
||||
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
||||
let torso_mat = self.torso.compute_base_matrix();
|
||||
|
||||
@ -77,13 +76,9 @@ impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr {
|
||||
}
|
||||
|
||||
impl Default for SkeletonAttr {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
fn default() -> Self { Self }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a comp::bird_small::Body> for SkeletonAttr {
|
||||
fn from(_body: &'a comp::bird_small::Body) -> Self {
|
||||
Self
|
||||
}
|
||||
fn from(_body: &'a comp::bird_small::Body) -> Self { Self }
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct RunAnimation;
|
||||
|
||||
impl Animation for RunAnimation {
|
||||
type Skeleton = BirdSmallSkeleton;
|
||||
type Dependency = (f32, f64);
|
||||
type Skeleton = BirdSmallSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -9,8 +9,8 @@ pub struct Input {
|
||||
pub struct AttackAnimation;
|
||||
|
||||
impl Animation for AttackAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
@ -75,7 +75,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Axe) => {
|
||||
next.l_hand.offset =
|
||||
Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0);
|
||||
@ -100,7 +100,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Hammer) => {
|
||||
next.l_hand.offset =
|
||||
Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0);
|
||||
@ -125,7 +125,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Staff) => {
|
||||
next.l_hand.offset =
|
||||
Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0);
|
||||
@ -150,7 +150,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Shield) => {
|
||||
next.l_hand.offset =
|
||||
Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0);
|
||||
@ -175,7 +175,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Bow) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, -2.0 + slow * 5.0, -1.0);
|
||||
next.l_hand.ori = Quaternion::rotation_x(PI / 2.0)
|
||||
@ -196,7 +196,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_y(0.4)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Dagger) => {
|
||||
next.l_hand.offset =
|
||||
Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0);
|
||||
@ -221,7 +221,7 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Debug(_)) => {
|
||||
next.l_hand.offset =
|
||||
Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0);
|
||||
@ -246,8 +246,8 @@ impl Animation for AttackAnimation {
|
||||
* Quaternion::rotation_x(0.0 + accel_med * -0.8)
|
||||
* Quaternion::rotation_y(0.0 + accel_med * -0.4);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7);
|
||||
next.l_shoulder.ori = Quaternion::rotation_x(0.0);
|
||||
|
@ -9,8 +9,8 @@ pub struct Input {
|
||||
pub struct BlockAnimation;
|
||||
|
||||
impl Animation for BlockAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
@ -78,7 +78,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Axe) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -103,7 +103,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Hammer) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, 3.5, 6.5);
|
||||
next.l_hand.ori = Quaternion::rotation_x(2.07)
|
||||
@ -124,7 +124,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(-1.35)
|
||||
* Quaternion::rotation_z(-0.85);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Staff) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -149,7 +149,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Shield) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -174,7 +174,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Bow) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -199,7 +199,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Dagger) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -220,7 +220,7 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Debug(_)) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, 3.5, 6.5);
|
||||
next.l_hand.ori = Quaternion::rotation_x(2.07)
|
||||
@ -241,8 +241,8 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(-1.35)
|
||||
* Quaternion::rotation_z(-0.85);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
//next.l_foot.offset = Vec3::new(-3.4, 0.3, 8.0 + wave_ultra_slow_cos * 0.1);
|
||||
//next.l_foot.ori = Quaternion::rotation_x(-0.3);
|
||||
|
@ -9,8 +9,8 @@ pub struct Input {
|
||||
pub struct BlockIdleAnimation;
|
||||
|
||||
impl Animation for BlockIdleAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
@ -77,7 +77,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Axe) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -102,7 +102,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Hammer) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5);
|
||||
next.l_hand.ori = Quaternion::rotation_x(2.07)
|
||||
@ -123,7 +123,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(-1.35)
|
||||
* Quaternion::rotation_z(-0.85);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Staff) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -148,7 +148,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Shield) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -173,7 +173,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Bow) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -198,7 +198,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Dagger) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -219,7 +219,7 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Debug(_)) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, 3.5 + wave_ultra_slow * 2.0, 6.5);
|
||||
next.l_hand.ori = Quaternion::rotation_x(2.07)
|
||||
@ -240,8 +240,8 @@ impl Animation for BlockIdleAnimation {
|
||||
* Quaternion::rotation_y(-1.35)
|
||||
* Quaternion::rotation_z(-0.85);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next.l_foot.offset = Vec3::new(-3.4, 0.3, 8.0 + wave_ultra_slow_cos * 0.1);
|
||||
next.l_foot.ori = Quaternion::rotation_x(-0.3);
|
||||
|
@ -9,8 +9,8 @@ pub struct Input {
|
||||
pub struct ChargeAnimation;
|
||||
|
||||
impl Animation for ChargeAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -10,8 +10,8 @@ pub struct Input {
|
||||
pub struct CidleAnimation;
|
||||
|
||||
impl Animation for CidleAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
@ -86,7 +86,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Axe) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.5 + wave_ultra_slow_cos * 1.0,
|
||||
@ -113,7 +113,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(-0.25)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Hammer) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0);
|
||||
next.l_hand.ori = Quaternion::rotation_x(1.27 + wave_ultra_slow * -0.1)
|
||||
@ -134,7 +134,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(-1.27)
|
||||
* Quaternion::rotation_z(wave_ultra_slow * 0.2);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Staff) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -159,7 +159,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Shield) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -184,7 +184,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Bow) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-1.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -213,7 +213,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(0.4)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Dagger) => {
|
||||
next.l_hand.offset = Vec3::new(
|
||||
-6.0 + wave_ultra_slow_cos * 1.0,
|
||||
@ -234,7 +234,7 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
},
|
||||
Some(Tool::Debug(_)) => {
|
||||
next.l_hand.offset = Vec3::new(-7.0, 4.0, 3.0);
|
||||
next.l_hand.ori = Quaternion::rotation_x(1.27 + wave_ultra_slow * -0.1)
|
||||
@ -255,8 +255,8 @@ impl Animation for CidleAnimation {
|
||||
* Quaternion::rotation_y(-1.27)
|
||||
* Quaternion::rotation_z(wave_ultra_slow * 0.2);
|
||||
next.main.scale = Vec3::one();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next.l_foot.offset = Vec3::new(-3.4, -1.5, 8.0 + wave_slow * 0.2);
|
||||
next.l_foot.ori = Quaternion::rotation_x(wave_ultra_slow_cos * 0.015);
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct ClimbAnimation;
|
||||
|
||||
impl Animation for ClimbAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, Vec3<f32>, Vec3<f32>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -6,8 +6,8 @@ use vek::*;
|
||||
pub struct GlidingAnimation;
|
||||
|
||||
impl Animation for GlidingAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, Vec3<f32>, Vec3<f32>, Vec3<f32>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -5,8 +5,8 @@ use vek::*;
|
||||
pub struct IdleAnimation;
|
||||
|
||||
impl Animation for IdleAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = f64;
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -6,8 +6,8 @@ use vek::*;
|
||||
pub struct JumpAnimation;
|
||||
|
||||
impl Animation for JumpAnimation {
|
||||
type Skeleton = CharacterSkeleton;
|
||||
type Dependency = (Option<Tool>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
fn update_skeleton(
|
||||
skeleton: &Self::Skeleton,
|
||||
|
@ -15,21 +15,13 @@ pub mod swim;
|
||||
pub mod wield;
|
||||
|
||||
// Reexports
|
||||
pub use self::attack::AttackAnimation;
|
||||
pub use self::block::BlockAnimation;
|
||||
pub use self::blockidle::BlockIdleAnimation;
|
||||
pub use self::charge::ChargeAnimation;
|
||||
pub use self::cidle::CidleAnimation;
|
||||
pub use self::climb::ClimbAnimation;
|
||||
pub use self::gliding::GlidingAnimation;
|
||||
pub use self::idle::IdleAnimation;
|
||||
pub use self::jump::JumpAnimation;
|
||||
pub use self::roll::RollAnimation;
|
||||
pub use self::run::RunAnimation;
|
||||
pub use self::sit::SitAnimation;
|
||||
pub use self::stand::StandAnimation;
|
||||
pub use self::swim::SwimAnimation;
|
||||
pub use self::wield::WieldAnimation;
|
||||
pub use self::{
|
||||
attack::AttackAnimation, block::BlockAnimation, blockidle::BlockIdleAnimation,
|
||||
charge::ChargeAnimation, cidle::CidleAnimation, climb::ClimbAnimation,
|
||||
gliding::GlidingAnimation, idle::IdleAnimation, jump::JumpAnimation, roll::RollAnimation,
|
||||
run::RunAnimation, sit::SitAnimation, stand::StandAnimation, swim::SwimAnimation,
|
||||
wield::WieldAnimation,
|
||||
};
|
||||
|
||||
use super::{Bone, Skeleton};
|
||||
use crate::render::FigureBoneData;
|
||||
@ -54,13 +46,12 @@ pub struct CharacterSkeleton {
|
||||
}
|
||||
|
||||
impl CharacterSkeleton {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
impl Skeleton for CharacterSkeleton {
|
||||
type Attr = SkeletonAttr;
|
||||
|
||||
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
||||
let chest_mat = self.chest.compute_base_matrix();
|
||||
let torso_mat = self.torso.compute_base_matrix();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user