Merge remote-tracking branch 'origin/master' into sharp/small-fixes

This commit is contained in:
Joshua Yanovski 2020-04-25 23:48:33 +02:00
commit bc2560ea90
50 changed files with 2313 additions and 1099 deletions

View File

@ -61,6 +61,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added waypoints next to dungeons - Added waypoints next to dungeons
- Made players spawn in towns - Made players spawn in towns
- Added non-uniform block heights - Added non-uniform block heights
- Added `/sudo` command
- Added a Level of Detail (LoD) system for terrain sprites and entities
### Changed ### Changed

View File

@ -252,7 +252,8 @@ Viel Spaß in der Welt von Veloren, Abenteurer!"#,
"hud.settings.invert_mouse_y_axis": "Maus Y-Achse invertieren", "hud.settings.invert_mouse_y_axis": "Maus Y-Achse invertieren",
"hud.settings.free_look_behavior": "Freies Umsehen", "hud.settings.free_look_behavior": "Freies Umsehen",
"hud.settings.view_distance": "Sichtweite", "hud.settings.view_distance": "Gelände Sichtweite",
"hud.settings.sprites_view_distance": "Objekt Sichtweite",
"hud.settings.maximum_fps": "Maximale FPS", "hud.settings.maximum_fps": "Maximale FPS",
"hud.settings.fov": "Sichtfeld (Grad)", "hud.settings.fov": "Sichtfeld (Grad)",
"hud.settings.gamma": "Gamma", "hud.settings.gamma": "Gamma",

View File

@ -250,6 +250,7 @@ Enjoy your stay in the World of Veloren."#,
"hud.settings.free_look_behavior": "Free look behavior", "hud.settings.free_look_behavior": "Free look behavior",
"hud.settings.view_distance": "View Distance", "hud.settings.view_distance": "View Distance",
"hud.settings.sprites_view_distance": "Sprites View Distance",
"hud.settings.maximum_fps": "Maximum FPS", "hud.settings.maximum_fps": "Maximum FPS",
"hud.settings.fov": "Field of View (deg)", "hud.settings.fov": "Field of View (deg)",
"hud.settings.gamma": "Gamma", "hud.settings.gamma": "Gamma",

View File

@ -3,11 +3,10 @@
#include <globals.glsl> #include <globals.glsl>
#include <lod.glsl> #include <lod.glsl>
in vec3 v_pos; in uint v_pos_norm;
in vec3 v_norm; in vec3 v_norm;
in vec3 v_col; in uint v_col;
in float v_ao; in uint v_ao_bone;
in uint v_bone_idx;
layout (std140) layout (std140)
uniform u_locals { uniform u_locals {
@ -24,6 +23,7 @@ struct BoneData {
layout (std140) layout (std140)
uniform u_bones { uniform u_bones {
// Warning: might not actually be 16 elements long. Don't index out of bounds!
BoneData bones[16]; BoneData bones[16];
}; };
@ -36,20 +36,27 @@ flat out vec3 f_norm;
void main() { void main() {
// Pre-calculate bone matrix // Pre-calculate bone matrix
mat4 combined_mat = model_mat * bones[v_bone_idx].bone_mat; uint bone_idx = (v_ao_bone >> 2) & 0x3Fu;
mat4 combined_mat = model_mat * bones[bone_idx].bone_mat;
vec3 pos = vec3((uvec3(v_pos_norm) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) - 128.0;
f_pos = ( f_pos = (
combined_mat * combined_mat *
vec4(v_pos, 1)).xyz; vec4(pos, 1)).xyz;
f_col = srgb_to_linear(v_col); f_col = srgb_to_linear(vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0);
f_ao = v_ao; f_ao = float(v_ao_bone & 0x3u) / 4.0;
// First 3 normals are negative, next 3 are positive
vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1));
vec3 norm = normals[(v_pos_norm >> 24) & 0x7u];
// Calculate normal here rather than for each pixel in the fragment shader // Calculate normal here rather than for each pixel in the fragment shader
f_norm = normalize(( f_norm = normalize((
combined_mat * combined_mat *
vec4(v_norm, 0.0) vec4(norm, 0.0)
).xyz); ).xyz);
// Also precalculate shadow texture and estimated terrain altitude. // Also precalculate shadow texture and estimated terrain altitude.

View File

@ -16,6 +16,7 @@ uniform u_globals {
// 0 - FirstPerson // 0 - FirstPerson
// 1 - ThirdPerson // 1 - ThirdPerson
uint cam_mode; uint cam_mode;
float sprite_render_distance;
}; };
// Specifies the pattern used in the player dithering // Specifies the pattern used in the player dithering
@ -26,4 +27,4 @@ mat4 threshold_matrix = mat4(
vec4(16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0) vec4(16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0)
); );
float distance_divider = 2; float distance_divider = 2;
float shadow_dithering = 0.5; float shadow_dithering = 0.5;

View File

@ -14,7 +14,6 @@ out vec4 tgt_color;
#include <light.glsl> #include <light.glsl>
#include <lod.glsl> #include <lod.glsl>
const float RENDER_DIST = 112.0;
const float FADE_DIST = 32.0; const float FADE_DIST = 32.0;
void main() { void main() {
@ -98,5 +97,5 @@ void main() {
vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds); vec3 fog_color = get_sky_color(cam_to_frag/*view_dir*/, time_of_day.x, cam_pos.xyz, f_pos, 0.5, true, clouds);
vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a); vec3 color = mix(mix(surf_color, fog_color, fog_level), clouds.rgb, clouds.a);
tgt_color = vec4(color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (RENDER_DIST - FADE_DIST)) / FADE_DIST, 0, 1)); tgt_color = vec4(color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (sprite_render_distance - FADE_DIST)) / FADE_DIST, 0, 1));
} }

View File

@ -4,9 +4,8 @@
#include <srgb.glsl> #include <srgb.glsl>
in vec3 v_pos; in vec3 v_pos;
in vec3 v_norm; in uint v_col;
in vec3 v_col; in uint v_norm_ao;
in float v_ao;
in vec4 inst_mat0; in vec4 inst_mat0;
in vec4 inst_mat1; in vec4 inst_mat1;
in vec4 inst_mat2; in vec4 inst_mat2;
@ -32,7 +31,7 @@ void main() {
vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz;
f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz; f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz;
f_pos.z -= min(32.0, pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0)); f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0));
// Wind waving // Wind waving
f_pos += inst_wind_sway * vec3( f_pos += inst_wind_sway * vec3(
@ -41,10 +40,13 @@ void main() {
0.0 0.0
) * pow(abs(v_pos.z) * SCALE, 1.3) * 0.2; ) * pow(abs(v_pos.z) * SCALE, 1.3) * 0.2;
f_norm = (inst_mat * vec4(v_norm, 0)).xyz; // First 3 normals are negative, next 3 are positive
vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1));
f_norm = (inst_mat * vec4(normals[(v_norm_ao >> 0) & 0x7u], 0)).xyz;
f_col = srgb_to_linear(v_col) * srgb_to_linear(inst_col); vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0;
f_ao = v_ao; f_col = srgb_to_linear(col) * srgb_to_linear(inst_col);
f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0;
// Select glowing // Select glowing
if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) { if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,7 +2,9 @@ pub mod cell;
pub mod mat_cell; pub mod mat_cell;
pub use mat_cell::Material; pub use mat_cell::Material;
use self::{cell::Cell, mat_cell::MatCell}; // Reexport
pub use self::{cell::Cell, mat_cell::MatCell};
use crate::{ use crate::{
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol}, vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol},
volumes::dyna::Dyna, volumes::dyna::Dyna,

View File

@ -1,4 +1,4 @@
use crate::ray::Ray; use crate::{ray::Ray, volumes::scaled::Scaled};
use std::fmt::Debug; use std::fmt::Debug;
use vek::*; use vek::*;
@ -24,6 +24,13 @@ pub trait Vox: Sized + Clone + PartialEq {
pub trait BaseVol { pub trait BaseVol {
type Vox: Vox; type Vox: Vox;
type Error: Debug; type Error: Debug;
fn scaled_by(&self, scale: Vec3<f32>) -> Scaled<Self>
where
Self: Sized,
{
Scaled { inner: self, scale }
}
} }
/// Implementing `BaseVol` for any `&'a BaseVol` makes it possible to implement /// Implementing `BaseVol` for any `&'a BaseVol` makes it possible to implement
@ -159,6 +166,7 @@ where
/// Unfortunately we can't just implement `IntoIterator` in this generic way /// Unfortunately we can't just implement `IntoIterator` in this generic way
/// because it's defined in another crate. That's actually the only reason why /// because it's defined in another crate. That's actually the only reason why
/// the trait `IntoFullVolIterator` exists. /// the trait `IntoFullVolIterator` exists.
// TODO: See whether relaxed orphan rules permit this to be replaced now
impl<'a, T: 'a + SizedVol> IntoFullVolIterator<'a> for &'a T impl<'a, T: 'a + SizedVol> IntoFullVolIterator<'a> for &'a T
where where
Self: IntoVolIterator<'a>, Self: IntoVolIterator<'a>,

View File

@ -1,4 +1,5 @@
pub mod chunk; pub mod chunk;
pub mod dyna; pub mod dyna;
pub mod scaled;
pub mod vol_grid_2d; pub mod vol_grid_2d;
pub mod vol_grid_3d; pub mod vol_grid_3d;

View File

@ -0,0 +1,53 @@
use crate::vol::{BaseVol, ReadVol, SizedVol, Vox};
use vek::*;
pub struct Scaled<'a, V> {
pub inner: &'a V,
pub scale: Vec3<f32>,
}
impl<'a, V: BaseVol> BaseVol for Scaled<'a, V> {
type Error = V::Error;
type Vox = V::Vox;
}
impl<'a, V: ReadVol> ReadVol for Scaled<'a, V> {
#[inline(always)]
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Error> {
let ideal_pos = pos.map2(self.scale, |e, scale| e as f32 / scale);
let pos = ideal_pos.map(|e| e.trunc() as i32);
let ideal_search_size = Vec3::<f32>::one() / self.scale;
let range_iter = |i: usize| {
std::iter::successors(Some(0), |p| Some(if *p < 0 { -*p } else { -(*p + 1) }))
.take_while(move |p| {
((ideal_pos[i] - ideal_search_size[i] / 2.0).round() as i32
..(ideal_pos[i] + ideal_search_size[i] / 2.0).round() as i32)
.contains(&(pos[i] + *p))
})
};
range_iter(0)
.map(|i| range_iter(1).map(move |j| range_iter(2).map(move |k| Vec3::new(i, j, k))))
.flatten()
.flatten()
.map(|offs| self.inner.get(pos + offs))
.find(|vox| vox.as_ref().map(|v| !v.is_empty()).unwrap_or(false))
.unwrap_or_else(|| self.inner.get(pos))
}
}
impl<'a, V: SizedVol> SizedVol for Scaled<'a, V> {
#[inline(always)]
fn lower_bound(&self) -> Vec3<i32> {
self.inner
.lower_bound()
.map2(self.scale, |e, scale| (e as f32 * scale).floor() as i32)
}
#[inline(always)]
fn upper_bound(&self) -> Vec3<i32> {
self.inner
.upper_bound()
.map2(self.scale, |e, scale| (e as f32 * scale).ceil() as i32 + 1)
}
}

View File

@ -40,12 +40,14 @@ pub struct ChatCommand {
/// * `&mut Server` - the `Server` instance executing the command. /// * `&mut Server` - the `Server` instance executing the command.
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the /// * `EcsEntity` - an `Entity` corresponding to the player that invoked the
/// command. /// command.
/// * `EcsEntity` - an `Entity` for the player on whom the command is
/// invoked. This differs from the previous argument when using /sudo
/// * `String` - a `String` containing the part of the command after the /// * `String` - a `String` containing the part of the command after the
/// keyword. /// keyword.
/// * `&ChatCommand` - the command to execute with the above arguments. /// * `&ChatCommand` - the command to execute with the above arguments.
/// Handler functions must parse arguments from the the given `String` /// Handler functions must parse arguments from the the given `String`
/// (`scan_fmt!` is included for this purpose). /// (`scan_fmt!` is included for this purpose).
handler: fn(&mut Server, EcsEntity, String, &ChatCommand), handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand),
} }
impl ChatCommand { impl ChatCommand {
@ -55,7 +57,7 @@ impl ChatCommand {
arg_fmt: &'static str, arg_fmt: &'static str,
help_string: &'static str, help_string: &'static str,
needs_admin: bool, needs_admin: bool,
handler: fn(&mut Server, EcsEntity, String, &ChatCommand), handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand),
) -> Self { ) -> Self {
Self { Self {
keyword, keyword,
@ -80,10 +82,10 @@ impl ChatCommand {
); );
return; return;
} else { } else {
(self.handler)(server, entity, args, self); (self.handler)(server, entity, entity, args, self);
} }
} else { } else {
(self.handler)(server, entity, args, self); (self.handler)(server, entity, entity, args, self);
} }
} }
} }
@ -261,79 +263,125 @@ lazy_static! {
true, true,
handle_debug, handle_debug,
), ),
ChatCommand::new(
"sudo",
"{} {} {/.*/}",
"/sudo <player> /<command> [args...] : Run command as if you were another player",
true,
handle_sudo,
),
]; ];
} }
fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { fn handle_give(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
_action: &ChatCommand,
) {
if let Ok(item) = assets::load_cloned(&args) { if let Ok(item) = assets::load_cloned(&args) {
server server
.state .state
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(target)
.map(|inv| inv.push(item)); .map(|inv| inv.push(item));
let _ = server let _ = server
.state .state
.ecs() .ecs()
.write_storage::<comp::InventoryUpdate>() .write_storage::<comp::InventoryUpdate>()
.insert( .insert(
entity, target,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given), comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given),
); );
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from("Invalid item!"))); server.notify_client(client, ServerMsg::private(String::from("Invalid item!")));
} }
} }
fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_jump(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(current_pos) => { Some(current_pos) => {
server server
.state .state
.write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); .write_component(target, comp::Pos(current_pos.0 + Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position.")), ServerMsg::private(String::from("You have no position.")),
), ),
} }
} }
} }
fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_goto(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
if server if server
.state .state
.read_component_cloned::<comp::Pos>(entity) .read_component_cloned::<comp::Pos>(target)
.is_some() .is_some()
{ {
server server
.state .state
.write_component(entity, comp::Pos(Vec3::new(x, y, z))); .write_component(target, comp::Pos(Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position.")), ServerMsg::private(String::from("You have no position.")),
); );
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_kill(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
let reason = if client == target {
comp::HealthSource::Suicide
} else {
if let Some(uid) = server.state.read_storage::<Uid>().get(client) {
comp::HealthSource::Attack { by: *uid }
} else {
comp::HealthSource::Command
}
};
server server
.state .state
.ecs_mut() .ecs_mut()
.write_storage::<comp::Stats>() .write_storage::<comp::Stats>()
.get_mut(entity) .get_mut(target)
.map(|s| s.health.set_to(0, comp::HealthSource::Suicide)); .map(|s| s.health.set_to(0, reason));
} }
fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_time(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let time = scan_fmt_some!(&args, action.arg_fmt, String); let time = scan_fmt_some!(&args, action.arg_fmt, String);
let new_time = match time.as_ref().map(|s| s.as_str()) { let new_time = match time.as_ref().map(|s| s.as_str()) {
Some("midnight") => NaiveTime::from_hms(0, 0, 0), Some("midnight") => NaiveTime::from_hms(0, 0, 0),
@ -349,7 +397,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
Ok(time) => time, Ok(time) => time,
Err(_) => { Err(_) => {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("'{}' is not a valid time.", n)), ServerMsg::private(format!("'{}' is not a valid time.", n)),
); );
return; return;
@ -368,7 +416,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
Some(time) => format!("It is {}", time.format("%H:%M").to_string()), Some(time) => format!("It is {}", time.format("%H:%M").to_string()),
None => String::from("Unknown Time"), None => String::from("Unknown Time"),
}; };
server.notify_client(entity, ServerMsg::private(msg)); server.notify_client(client, ServerMsg::private(msg));
return; return;
}, },
}; };
@ -377,7 +425,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
new_time.num_seconds_from_midnight() as f64; new_time.num_seconds_from_midnight() as f64;
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!( ServerMsg::private(format!(
"Time changed to: {}", "Time changed to: {}",
new_time.format("%H:%M").to_string() new_time.format("%H:%M").to_string()
@ -385,43 +433,55 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
); );
} }
fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_health(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) {
if let Some(stats) = server if let Some(stats) = server
.state .state
.ecs() .ecs()
.write_storage::<comp::Stats>() .write_storage::<comp::Stats>()
.get_mut(entity) .get_mut(target)
{ {
stats.health.set_to(hp, comp::HealthSource::Command); stats.health.set_to(hp, comp::HealthSource::Command);
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no health.")), ServerMsg::private(String::from("You have no health.")),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You must specify health amount!")), ServerMsg::private(String::from("You must specify health amount!")),
); );
} }
} }
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_alias(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
server server
.state .state
.ecs_mut() .ecs_mut()
.write_storage::<comp::Player>() .write_storage::<comp::Player>()
.get_mut(entity) .get_mut(target)
.map(|player| player.alias = alias); .map(|player| player.alias = alias);
// Update name on client player lists // Update name on client player lists
let ecs = server.state.ecs(); let ecs = server.state.ecs();
if let (Some(uid), Some(player)) = ( if let (Some(uid), Some(player)) = (
ecs.read_storage::<Uid>().get(entity), ecs.read_storage::<Uid>().get(target),
ecs.read_storage::<comp::Player>().get(entity), ecs.read_storage::<comp::Player>().get(target),
) { ) {
let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias( let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias(
(*uid).into(), (*uid).into(),
@ -430,60 +490,72 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C
server.state.notify_registered_clients(msg); server.state.notify_registered_clients(msg);
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_tp(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>()) let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join() .join()
.find(|(_, player)| player.alias == alias) .find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity); .map(|(entity, _)| entity);
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(_pos) => match opt_player { Some(_pos) => match opt_player {
Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) { Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) {
Some(pos) => { Some(pos) => {
server.state.write_component(entity, pos); server.state.write_component(target, pos);
server.state.write_component(entity, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)), ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)),
), ),
}, },
None => { None => {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Player '{}' not found!", alias)), ServerMsg::private(format!("Player '{}' not found!", alias)),
); );
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from(action.help_string)), ServerMsg::private(String::from(action.help_string)),
); );
}, },
}, },
None => { None => {
server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
}, },
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_spawn(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
match scan_fmt_some!(&args, action.arg_fmt, String, npc::NpcBody, String) { match scan_fmt_some!(&args, action.arg_fmt, String, npc::NpcBody, String) {
(Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount) => { (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount) => {
if let Some(alignment) = parse_alignment(entity, &opt_align) { if let Some(alignment) = parse_alignment(target, &opt_align) {
let amount = opt_amount let amount = opt_amount
.and_then(|a| a.parse().ok()) .and_then(|a| a.parse().ok())
.filter(|x| *x > 0) .filter(|x| *x > 0)
.unwrap_or(1) .unwrap_or(1)
.min(10); .min(10);
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let agent = let agent =
if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment {
@ -517,7 +589,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
if let Some(uid) = server.state.ecs().uid_from_entity(new_entity) { if let Some(uid) = server.state.ecs().uid_from_entity(new_entity) {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private( ServerMsg::private(
format!("Spawned entity with ID: {}", uid).to_owned(), format!("Spawned entity with ID: {}", uid).to_owned(),
), ),
@ -525,24 +597,30 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
} }
} }
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Spawned {} entities", amount).to_owned()), ServerMsg::private(format!("Spawned {} entities", amount).to_owned()),
); );
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private("You have no position!".to_owned()), ServerMsg::private("You have no position!".to_owned()),
), ),
} }
} }
}, },
_ => { _ => {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
}, },
} }
} }
fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_players(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let players = ecs.read_storage::<comp::Player>(); let players = ecs.read_storage::<comp::Player>();
let count = players.join().count(); let count = players.join().count();
@ -560,26 +638,32 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action
s s
}); });
server.notify_client(entity, ServerMsg::private(header_message + &player_list)); server.notify_client(client, ServerMsg::private(header_message + &player_list));
} else { } else {
server.notify_client(entity, ServerMsg::private(header_message)); server.notify_client(client, ServerMsg::private(header_message));
} }
} }
fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_build(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
if server if server
.state .state
.read_storage::<comp::CanBuild>() .read_storage::<comp::CanBuild>()
.get(entity) .get(target)
.is_some() .is_some()
{ {
server server
.state .state
.ecs() .ecs()
.write_storage::<comp::CanBuild>() .write_storage::<comp::CanBuild>()
.remove(entity); .remove(target);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Toggled off build mode!")), ServerMsg::private(String::from("Toggled off build mode!")),
); );
} else { } else {
@ -587,18 +671,24 @@ fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action:
.state .state
.ecs() .ecs()
.write_storage::<comp::CanBuild>() .write_storage::<comp::CanBuild>()
.insert(entity, comp::CanBuild); .insert(target, comp::CanBuild);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Toggled on build mode!")), ServerMsg::private(String::from("Toggled on build mode!")),
); );
} }
} }
fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_help(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
for cmd in CHAT_COMMANDS.iter() { for cmd in CHAT_COMMANDS.iter() {
if !cmd.needs_admin || server.entity_is_admin(entity) { if !cmd.needs_admin || server.entity_is_admin(client) {
server.notify_client(entity, ServerMsg::private(String::from(cmd.help_string))); server.notify_client(client, ServerMsg::private(String::from(cmd.help_string)));
} }
} }
} }
@ -613,7 +703,13 @@ fn parse_alignment(owner: EcsEntity, alignment: &str) -> Option<comp::Alignment>
} }
} }
fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_killnpcs(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let mut stats = ecs.write_storage::<comp::Stats>(); let mut stats = ecs.write_storage::<comp::Stats>();
let players = ecs.read_storage::<comp::Player>(); let players = ecs.read_storage::<comp::Player>();
@ -627,23 +723,29 @@ fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _actio
} else { } else {
"No NPCs on server.".to_string() "No NPCs on server.".to_string()
}; };
server.notify_client(entity, ServerMsg::private(text)); server.notify_client(client, ServerMsg::private(text));
} }
fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { fn handle_object(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
_action: &ChatCommand,
) {
let obj_type = scan_fmt!(&args, _action.arg_fmt, String); let obj_type = scan_fmt!(&args, _action.arg_fmt, String);
let pos = server let pos = server
.state .state
.ecs() .ecs()
.read_storage::<comp::Pos>() .read_storage::<comp::Pos>()
.get(entity) .get(target)
.copied(); .copied();
let ori = server let ori = server
.state .state
.ecs() .ecs()
.read_storage::<comp::Ori>() .read_storage::<comp::Ori>()
.get(entity) .get(target)
.copied(); .copied();
/*let builder = server.state /*let builder = server.state
.create_object(pos, ori, obj_type) .create_object(pos, ori, obj_type)
@ -701,7 +803,7 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
Ok("crafting_bench") => comp::object::Body::CraftingBench, Ok("crafting_bench") => comp::object::Body::CraftingBench,
_ => { _ => {
return server.notify_client( return server.notify_client(
entity, client,
ServerMsg::private(String::from("Object not found!")), ServerMsg::private(String::from("Object not found!")),
); );
}, },
@ -723,18 +825,24 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
)) ))
.build(); .build();
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!( ServerMsg::private(format!(
"Spawned: {}", "Spawned: {}",
obj_str_res.unwrap_or("<Unknown object>") obj_str_res.unwrap_or("<Unknown object>")
)), )),
); );
} else { } else {
server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
} }
} }
fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_light(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (opt_r, opt_g, opt_b, opt_x, opt_y, opt_z, opt_s) = let (opt_r, opt_g, opt_b, opt_x, opt_y, opt_z, opt_s) =
scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32, f32, f32, f32); scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32, f32, f32, f32);
@ -756,7 +864,7 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C
.state .state
.ecs() .ecs()
.read_storage::<comp::Pos>() .read_storage::<comp::Pos>()
.get(entity) .get(target)
.copied(); .copied();
if let Some(pos) = pos { if let Some(pos) = pos {
server server
@ -767,19 +875,25 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C
.with(comp::ForceUpdate) .with(comp::ForceUpdate)
.with(light_emitter) .with(light_emitter)
.build(); .build();
server.notify_client(entity, ServerMsg::private(format!("Spawned object."))); server.notify_client(client, ServerMsg::private(format!("Spawned object.")));
} else { } else {
server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
} }
} }
fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_lantern(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let opt_s = scan_fmt_some!(&args, action.arg_fmt, f32); let opt_s = scan_fmt_some!(&args, action.arg_fmt, f32);
if server if server
.state .state
.read_storage::<comp::LightEmitter>() .read_storage::<comp::LightEmitter>()
.get(entity) .get(target)
.is_some() .is_some()
{ {
if let Some(s) = opt_s { if let Some(s) = opt_s {
@ -787,11 +901,11 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.state .state
.ecs() .ecs()
.write_storage::<comp::LightEmitter>() .write_storage::<comp::LightEmitter>()
.get_mut(entity) .get_mut(target)
{ {
light.strength = s.max(0.1).min(10.0); light.strength = s.max(0.1).min(10.0);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You adjusted flame strength.")), ServerMsg::private(String::from("You adjusted flame strength.")),
); );
} }
@ -800,9 +914,9 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.state .state
.ecs() .ecs()
.write_storage::<comp::LightEmitter>() .write_storage::<comp::LightEmitter>()
.remove(entity); .remove(target);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You put out the lantern.")), ServerMsg::private(String::from("You put out the lantern.")),
); );
} }
@ -811,7 +925,7 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.state .state
.ecs() .ecs()
.write_storage::<comp::LightEmitter>() .write_storage::<comp::LightEmitter>()
.insert(entity, comp::LightEmitter { .insert(target, comp::LightEmitter {
offset: Vec3::new(0.5, 0.2, 0.8), offset: Vec3::new(0.5, 0.2, 0.8),
col: Rgb::new(1.0, 0.75, 0.3), col: Rgb::new(1.0, 0.75, 0.3),
strength: if let Some(s) = opt_s { strength: if let Some(s) = opt_s {
@ -822,50 +936,68 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
}); });
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You lit your lantern.")), ServerMsg::private(String::from("You lit your lantern.")),
); );
} }
} }
fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_explosion(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let power = scan_fmt!(&args, action.arg_fmt, f32).unwrap_or(8.0); let power = scan_fmt!(&args, action.arg_fmt, f32).unwrap_or(8.0);
let ecs = server.state.ecs(); let ecs = server.state.ecs();
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
ecs.read_resource::<EventBus<ServerEvent>>() ecs.read_resource::<EventBus<ServerEvent>>()
.emit_now(ServerEvent::Explosion { .emit_now(ServerEvent::Explosion {
pos: pos.0, pos: pos.0,
power, power,
owner: ecs.read_storage::<Uid>().get(entity).copied(), owner: ecs.read_storage::<Uid>().get(target).copied(),
}) })
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position!")), ServerMsg::private(String::from("You have no position!")),
), ),
} }
} }
fn handle_waypoint(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_waypoint(
match server.state.read_component_cloned::<comp::Pos>(entity) { server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let _ = server let _ = server
.state .state
.ecs() .ecs()
.write_storage::<comp::Waypoint>() .write_storage::<comp::Waypoint>()
.insert(entity, comp::Waypoint::new(pos.0)); .insert(target, comp::Waypoint::new(pos.0));
server.notify_client(entity, ServerMsg::private(String::from("Waypoint set!"))); server.notify_client(client, ServerMsg::private(String::from("Waypoint set!")));
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position!")), ServerMsg::private(String::from("You have no position!")),
), ),
} }
} }
fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_adminify(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>()) let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
@ -883,18 +1015,31 @@ fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action:
}, },
None => { None => {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Player '{}' not found!", alias)), ServerMsg::private(format!("Player '{}' not found!", alias)),
); );
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
}, },
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_tell(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if client != target {
server.notify_client(
client,
ServerMsg::tell(String::from("It's rude to impersonate people")),
);
return;
}
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let msg = &args[alias.len()..args.len()]; let msg = &args[alias.len()..args.len()];
@ -903,11 +1048,11 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
.find(|(_, player)| player.alias == alias) .find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity) .map(|(entity, _)| entity)
{ {
if player != entity { if player != target {
if msg.len() > 1 { if msg.len() > 1 {
if let Some(name) = ecs if let Some(name) = ecs
.read_storage::<comp::Player>() .read_storage::<comp::Player>()
.get(entity) .get(target)
.map(|s| s.alias.clone()) .map(|s| s.alias.clone())
{ {
server.notify_client( server.notify_client(
@ -915,53 +1060,60 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
ServerMsg::tell(format!("[{}] tells:{}", name, msg)), ServerMsg::tell(format!("[{}] tells:{}", name, msg)),
); );
server.notify_client( server.notify_client(
entity, client,
ServerMsg::tell(format!("To [{}]:{}", alias, msg)), ServerMsg::tell(format!("To [{}]:{}", alias, msg)),
); );
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Failed to send message.")), ServerMsg::private(String::from("Failed to send message.")),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("[{}] wants to talk to you.", alias)), ServerMsg::private(format!("[{}] wants to talk to you.", alias)),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("You can't /tell yourself.")), ServerMsg::private(format!("You can't /tell yourself.")),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Player '{}' not found!", alias)), ServerMsg::private(format!("Player '{}' not found!", alias)),
); );
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
#[cfg(not(feature = "worldgen"))] #[cfg(not(feature = "worldgen"))]
fn handle_debug_column( fn handle_debug_column(
server: &mut Server, server: &mut Server,
entity: EcsEntity, client: EcsEntity,
target: EcsEntity,
_args: String, _args: String,
_action: &ChatCommand, _action: &ChatCommand,
) { ) {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Unsupported without worldgen enabled")), ServerMsg::private(String::from("Unsupported without worldgen enabled")),
); );
} }
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
fn handle_debug_column(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_debug_column(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let sim = server.world.sim(); let sim = server.world.sim();
let sampler = server.world.sample_columns(); let sampler = server.world.sample_columns();
if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) { if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) {
@ -1020,15 +1172,15 @@ spawn_rate {:?} "#,
)) ))
}; };
if let Some(s) = foo() { if let Some(s) = foo() {
server.notify_client(entity, ServerMsg::private(s)); server.notify_client(client, ServerMsg::private(s));
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Not a pregenerated chunk.")), ServerMsg::private(String::from("Not a pregenerated chunk.")),
); );
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
@ -1048,12 +1200,18 @@ fn find_target(
} }
} }
fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_exp(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String); let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String);
if let Some(exp) = a_exp { if let Some(exp) = a_exp {
let ecs = server.state.ecs_mut(); let ecs = server.state.ecs_mut();
let target = find_target(&ecs, a_alias, entity); let target = find_target(&ecs, a_alias, target);
let mut error_msg = None; let mut error_msg = None;
@ -1071,17 +1229,23 @@ fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &Cha
} }
if let Some(msg) = error_msg { if let Some(msg) = error_msg {
server.notify_client(entity, msg); server.notify_client(client, msg);
} }
} }
} }
fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_level(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String); let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String);
if let Some(lvl) = a_lvl { if let Some(lvl) = a_lvl {
let ecs = server.state.ecs_mut(); let ecs = server.state.ecs_mut();
let target = find_target(&ecs, a_alias, entity); let target = find_target(&ecs, a_alias, target);
let mut error_msg = None; let mut error_msg = None;
@ -1104,19 +1268,25 @@ fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &C
} }
if let Some(msg) = error_msg { if let Some(msg) = error_msg {
server.notify_client(entity, msg); server.notify_client(client, msg);
} }
} }
} }
use common::comp::Item; use common::comp::Item;
fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_debug(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
if let Ok(items) = assets::load_glob::<Item>("common.items.debug.*") { if let Ok(items) = assets::load_glob::<Item>("common.items.debug.*") {
server server
.state() .state()
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(target)
// TODO: Consider writing a `load_glob_cloned` in `assets` and using that here // TODO: Consider writing a `load_glob_cloned` in `assets` and using that here
.map(|inv| inv.push_all_unique(items.iter().map(|item| item.as_ref().clone()))); .map(|inv| inv.push_all_unique(items.iter().map(|item| item.as_ref().clone())));
let _ = server let _ = server
@ -1124,12 +1294,12 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action:
.ecs() .ecs()
.write_storage::<comp::InventoryUpdate>() .write_storage::<comp::InventoryUpdate>()
.insert( .insert(
entity, target,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug),
); );
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from( ServerMsg::private(String::from(
"Debug items not found? Something is very broken.", "Debug items not found? Something is very broken.",
)), )),
@ -1139,12 +1309,13 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action:
fn handle_remove_lights( fn handle_remove_lights(
server: &mut Server, server: &mut Server,
entity: EcsEntity, client: EcsEntity,
target: EcsEntity,
args: String, args: String,
action: &ChatCommand, action: &ChatCommand,
) { ) {
let opt_radius = scan_fmt_some!(&args, action.arg_fmt, f32); let opt_radius = scan_fmt_some!(&args, action.arg_fmt, f32);
let opt_player_pos = server.state.read_component_cloned::<comp::Pos>(entity); let opt_player_pos = server.state.read_component_cloned::<comp::Pos>(target);
let mut to_delete = vec![]; let mut to_delete = vec![];
match opt_player_pos { match opt_player_pos {
@ -1168,7 +1339,7 @@ fn handle_remove_lights(
} }
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position.")), ServerMsg::private(String::from("You have no position.")),
), ),
} }
@ -1182,7 +1353,48 @@ fn handle_remove_lights(
} }
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from(format!("Removed {} lights!", size))), ServerMsg::private(String::from(format!("Removed {} lights!", size))),
); );
} }
fn handle_sudo(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let (Some(player_alias), Some(cmd), cmd_args) =
scan_fmt_some!(&args, action.arg_fmt, String, String, String)
{
let cmd_args = cmd_args.unwrap_or(String::from(""));
let cmd = if cmd.chars().next() == Some('/') {
cmd.chars().skip(1).collect()
} else {
cmd
};
if let Some(action) = CHAT_COMMANDS.iter().find(|c| c.keyword == cmd) {
let ecs = server.state.ecs();
let entity_opt = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == player_alias)
.map(|(entity, _)| entity);
if let Some(entity) = entity_opt {
(action.handler)(server, client, entity, cmd_args, action);
} else {
server.notify_client(
client,
ServerMsg::private(format!("Could not find that player")),
);
}
} else {
server.notify_client(
client,
ServerMsg::private(format!("Unknown command: /{}", cmd)),
);
}
} else {
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
}
}

View File

@ -47,6 +47,8 @@ impl BipedLargeSkeleton {
impl Skeleton for BipedLargeSkeleton { impl Skeleton for BipedLargeSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 11 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let upper_torso_mat = self.upper_torso.compute_base_matrix(); let upper_torso_mat = self.upper_torso.compute_base_matrix();
let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); let shoulder_l_mat = self.shoulder_l.compute_base_matrix();

View File

@ -27,6 +27,8 @@ impl BirdMediumSkeleton {
impl Skeleton for BirdMediumSkeleton { impl Skeleton for BirdMediumSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 7 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let torso_mat = self.torso.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix();

View File

@ -31,6 +31,8 @@ impl BirdSmallSkeleton {
impl Skeleton for BirdSmallSkeleton { impl Skeleton for BirdSmallSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 4 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let torso_mat = self.torso.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix();

View File

@ -62,6 +62,8 @@ impl CharacterSkeleton {
impl Skeleton for CharacterSkeleton { impl Skeleton for CharacterSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 15 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let chest_mat = self.chest.compute_base_matrix(); let chest_mat = self.chest.compute_base_matrix();
let torso_mat = self.torso.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix();

View File

@ -32,6 +32,8 @@ impl CritterSkeleton {
impl Skeleton for CritterSkeleton { impl Skeleton for CritterSkeleton {
type Attr = CritterAttr; type Attr = CritterAttr;
fn bone_count(&self) -> usize { 5 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
[ [
FigureBoneData::new(self.head.compute_base_matrix()), FigureBoneData::new(self.head.compute_base_matrix()),

View File

@ -49,6 +49,8 @@ impl DragonSkeleton {
impl Skeleton for DragonSkeleton { impl Skeleton for DragonSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 13 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let chest_front_mat = self.chest_front.compute_base_matrix(); let chest_front_mat = self.chest_front.compute_base_matrix();
let wing_in_l_mat = self.wing_in_l.compute_base_matrix(); let wing_in_l_mat = self.wing_in_l.compute_base_matrix();

View File

@ -35,6 +35,8 @@ impl FishMediumSkeleton {
impl Skeleton for FishMediumSkeleton { impl Skeleton for FishMediumSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 6 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let torso_mat = self.torso.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix();
let rear_mat = self.rear.compute_base_matrix(); let rear_mat = self.rear.compute_base_matrix();

View File

@ -27,6 +27,8 @@ impl FishSmallSkeleton {
impl Skeleton for FishSmallSkeleton { impl Skeleton for FishSmallSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 2 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let torso_mat = self.torso.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix();

View File

@ -13,9 +13,11 @@ impl FixtureSkeleton {
impl Skeleton for FixtureSkeleton { impl Skeleton for FixtureSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 1 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
[ [
FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()), // <-- This is actually a bone!
FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()),

View File

@ -52,6 +52,8 @@ impl Bone {
pub trait Skeleton: Send + Sync + 'static { pub trait Skeleton: Send + Sync + 'static {
type Attr; type Attr;
fn bone_count(&self) -> usize { 16 }
fn compute_matrices(&self) -> [FigureBoneData; 16]; fn compute_matrices(&self) -> [FigureBoneData; 16];
/// Change the current skeleton to be more like `target`. /// Change the current skeleton to be more like `target`.

View File

@ -15,24 +15,26 @@ const SCALE: f32 = 1.0 / 11.0;
impl Skeleton for ObjectSkeleton { impl Skeleton for ObjectSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 1 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
[ [
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(vek::Mat4::identity()),
] ]
} }

View File

@ -31,6 +31,8 @@ impl QuadrupedMediumSkeleton {
impl Skeleton for QuadrupedMediumSkeleton { impl Skeleton for QuadrupedMediumSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 11 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
let ears_mat = self.ears.compute_base_matrix(); let ears_mat = self.ears.compute_base_matrix();
let head_upper_mat = self.head_upper.compute_base_matrix(); let head_upper_mat = self.head_upper.compute_base_matrix();

View File

@ -26,6 +26,8 @@ impl QuadrupedSmallSkeleton {
impl Skeleton for QuadrupedSmallSkeleton { impl Skeleton for QuadrupedSmallSkeleton {
type Attr = SkeletonAttr; type Attr = SkeletonAttr;
fn bone_count(&self) -> usize { 6 }
fn compute_matrices(&self) -> [FigureBoneData; 16] { fn compute_matrices(&self) -> [FigureBoneData; 16] {
[ [
FigureBoneData::new(self.head.compute_base_matrix()), FigureBoneData::new(self.head.compute_base_matrix()),

View File

@ -211,6 +211,7 @@ pub enum Event {
ToggleSmoothPan(bool), ToggleSmoothPan(bool),
AdjustViewDistance(u32), AdjustViewDistance(u32),
AdjustLodDetail(u32), AdjustLodDetail(u32),
AdjustSpriteRenderDistance(u32),
AdjustMusicVolume(f32), AdjustMusicVolume(f32),
AdjustSfxVolume(f32), AdjustSfxVolume(f32),
ChangeAudioDevice(String), ChangeAudioDevice(String),
@ -1805,6 +1806,9 @@ impl Hud {
settings_window::Event::AdjustLodDetail(lod_detail) => { settings_window::Event::AdjustLodDetail(lod_detail) => {
events.push(Event::AdjustLodDetail(lod_detail)); events.push(Event::AdjustLodDetail(lod_detail));
}, },
settings_window::Event::AdjustSpriteRenderDistance(view_distance) => {
events.push(Event::AdjustSpriteRenderDistance(view_distance));
},
settings_window::Event::CrosshairTransp(crosshair_transp) => { settings_window::Event::CrosshairTransp(crosshair_transp) => {
events.push(Event::CrosshairTransp(crosshair_transp)); events.push(Event::CrosshairTransp(crosshair_transp));
}, },

View File

@ -92,6 +92,9 @@ widget_ids! {
lod_detail_slider, lod_detail_slider,
lod_detail_text, lod_detail_text,
lod_detail_value, lod_detail_value,
sprite_dist_slider,
sprite_dist_text,
sprite_dist_value,
max_fps_slider, max_fps_slider,
max_fps_text, max_fps_text,
max_fps_value, max_fps_value,
@ -212,6 +215,7 @@ pub enum Event {
ToggleMouseYInvert(bool), ToggleMouseYInvert(bool),
ToggleSmoothPan(bool), ToggleSmoothPan(bool),
AdjustViewDistance(u32), AdjustViewDistance(u32),
AdjustSpriteRenderDistance(u32),
AdjustFOV(u16), AdjustFOV(u16),
AdjustLodDetail(u32), AdjustLodDetail(u32),
AdjustGamma(f32), AdjustGamma(f32),
@ -1654,7 +1658,43 @@ impl<'a> Widget for SettingsWindow<'a> {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.gamma_value, ui); .set(state.ids.gamma_value, ui);
// Sprites VD
if let Some(new_val) = ImageSlider::discrete(
self.global_state.settings.graphics.sprite_render_distance,
50,
500,
self.imgs.slider_indicator,
self.imgs.slider,
)
.w_h(104.0, 22.0)
.right_from(state.ids.vd_slider, 50.0)
.track_breadth(12.0)
.slider_length(10.0)
.pad_track((5.0, 5.0))
.set(state.ids.sprite_dist_slider, ui)
{
events.push(Event::AdjustSpriteRenderDistance(new_val));
}
Text::new(
&self
.localized_strings
.get("hud.settings.sprites_view_distance"),
)
.up_from(state.ids.sprite_dist_slider, 8.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.sprite_dist_text, ui);
Text::new(&format!(
"{}",
self.global_state.settings.graphics.sprite_render_distance
))
.right_from(state.ids.sprite_dist_slider, 8.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.sprite_dist_value, ui);
// AaMode // AaMode
Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode")) Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode"))
.down_from(state.ids.gamma_slider, 8.0) .down_from(state.ids.gamma_slider, 8.0)

View File

@ -4,14 +4,14 @@ mod vol;
use crate::render::{self, Mesh}; use crate::render::{self, Mesh};
pub trait Meshable<P: render::Pipeline, T: render::Pipeline> { pub trait Meshable<'a, P: render::Pipeline, T: render::Pipeline> {
type Pipeline: render::Pipeline; type Pipeline: render::Pipeline;
type TranslucentPipeline: render::Pipeline; type TranslucentPipeline: render::Pipeline;
type Supplement; type Supplement;
// Generate meshes - one opaque, one translucent // Generate meshes - one opaque, one translucent
fn generate_mesh( fn generate_mesh(
&self, &'a self,
supp: Self::Supplement, supp: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>); ) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>);
} }

View File

@ -3,27 +3,43 @@ use crate::{
render::{self, FigurePipeline, Mesh, SpritePipeline}, render::{self, FigurePipeline, Mesh, SpritePipeline},
}; };
use common::{ use common::{
figure::Segment, figure::Cell,
util::{linear_to_srgb, srgb_to_linear}, util::{linear_to_srgb, srgb_to_linear},
vol::{IntoFullVolIterator, ReadVol, Vox}, vol::{BaseVol, ReadVol, SizedVol, Vox},
}; };
use vek::*; use vek::*;
type FigureVertex = <FigurePipeline as render::Pipeline>::Vertex; type FigureVertex = <FigurePipeline as render::Pipeline>::Vertex;
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex; type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
impl Meshable<FigurePipeline, FigurePipeline> for Segment { impl<'a, V: 'a> Meshable<'a, FigurePipeline, FigurePipeline> for V
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = FigurePipeline; type Pipeline = FigurePipeline;
type Supplement = Vec3<f32>; type Supplement = (Vec3<f32>, Vec3<f32>);
type TranslucentPipeline = FigurePipeline; type TranslucentPipeline = FigurePipeline;
fn generate_mesh( fn generate_mesh(
&self, &'a self,
offs: Self::Supplement, (offs, scale): Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) { ) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut mesh = Mesh::new(); let mut mesh = Mesh::new();
for (pos, vox) in self.full_vol_iter() { let vol_iter = (self.lower_bound().x..self.upper_bound().x)
.map(|i| {
(self.lower_bound().y..self.upper_bound().y).map(move |j| {
(self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k))
})
})
.flatten()
.flatten()
.map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty())));
for (pos, vox) in vol_iter {
if let Some(col) = vox.get_color() { if let Some(col) = vox.get_color() {
vol::push_vox_verts( vol::push_vox_verts(
&mut mesh, &mut mesh,
@ -32,7 +48,7 @@ impl Meshable<FigurePipeline, FigurePipeline> for Segment {
&[[[Rgba::from_opaque(col); 3]; 3]; 3], &[[[Rgba::from_opaque(col); 3]; 3]; 3],
|origin, norm, col, light, ao| { |origin, norm, col, light, ao| {
FigureVertex::new( FigureVertex::new(
origin, origin * scale,
norm, norm,
linear_to_srgb(srgb_to_linear(col) * light), linear_to_srgb(srgb_to_linear(col) * light),
ao, ao,
@ -62,18 +78,34 @@ impl Meshable<FigurePipeline, FigurePipeline> for Segment {
} }
} }
impl Meshable<SpritePipeline, SpritePipeline> for Segment { impl<'a, V: 'a> Meshable<'a, SpritePipeline, SpritePipeline> for V
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = SpritePipeline; type Pipeline = SpritePipeline;
type Supplement = Vec3<f32>; type Supplement = (Vec3<f32>, Vec3<f32>);
type TranslucentPipeline = SpritePipeline; type TranslucentPipeline = SpritePipeline;
fn generate_mesh( fn generate_mesh(
&self, &'a self,
offs: Self::Supplement, (offs, scale): Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) { ) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut mesh = Mesh::new(); let mut mesh = Mesh::new();
for (pos, vox) in self.full_vol_iter() { let vol_iter = (self.lower_bound().x..self.upper_bound().x)
.map(|i| {
(self.lower_bound().y..self.upper_bound().y).map(move |j| {
(self.lower_bound().z..self.upper_bound().z).map(move |k| Vec3::new(i, j, k))
})
})
.flatten()
.flatten()
.map(|pos| (pos, self.get(pos).map(|x| *x).unwrap_or(Vox::empty())));
for (pos, vox) in vol_iter {
if let Some(col) = vox.get_color() { if let Some(col) = vox.get_color() {
vol::push_vox_verts( vol::push_vox_verts(
&mut mesh, &mut mesh,
@ -82,7 +114,7 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
&[[[Rgba::from_opaque(col); 3]; 3]; 3], &[[[Rgba::from_opaque(col); 3]; 3]; 3],
|origin, norm, col, light, ao| { |origin, norm, col, light, ao| {
SpriteVertex::new( SpriteVertex::new(
origin, origin * scale,
norm, norm,
linear_to_srgb(srgb_to_linear(col) * light), linear_to_srgb(srgb_to_linear(col) * light),
ao, ao,

View File

@ -198,15 +198,15 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
} }
} }
impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeline, FluidPipeline> impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
for VolGrid2d<V> Meshable<'a, TerrainPipeline, FluidPipeline> for VolGrid2d<V>
{ {
type Pipeline = TerrainPipeline; type Pipeline = TerrainPipeline;
type Supplement = Aabb<i32>; type Supplement = Aabb<i32>;
type TranslucentPipeline = FluidPipeline; type TranslucentPipeline = FluidPipeline;
fn generate_mesh( fn generate_mesh(
&self, &'a self,
range: Self::Supplement, range: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) { ) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut light = calc_light(range, self); let mut light = calc_light(range, self);

View File

@ -11,11 +11,12 @@ use vek::*;
gfx_defines! { gfx_defines! {
vertex Vertex { vertex Vertex {
pos: [f32; 3] = "v_pos", pos_norm: u32 = "v_pos_norm",
norm: [f32; 3] = "v_norm", col: u32 = "v_col",
col: [f32; 3] = "v_col", // BBBBBBAA
ao: f32 = "v_ao", // B = Bone
bone_idx: u8 = "v_bone_idx", // A = AO
ao_bone: u8 = "v_ao_bone",
} }
constant Locals { constant Locals {
@ -49,17 +50,29 @@ gfx_defines! {
impl Vertex { impl Vertex {
pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>, ao: f32, bone_idx: u8) -> Self { pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>, ao: f32, bone_idx: u8) -> Self {
let norm_bits = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 }
} else if norm.y != 0.0 {
if norm.y < 0.0 { 2 } else { 3 }
} else {
if norm.z < 0.0 { 4 } else { 5 }
};
Self { Self {
pos: pos.into_array(), pos_norm: pos
col: col.into_array(), .map2(Vec3::new(0, 8, 16), |e, shift| {
norm: norm.into_array(), ((e + 128.0) as u32) << shift
ao, })
bone_idx, .reduce_bitor()
| (norm_bits << 24),
col: col
.map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift)
.reduce_bitor(),
ao_bone: (bone_idx << 2) | ((ao * 3.9999) as u8),
} }
} }
pub fn with_bone_idx(mut self, bone_idx: u8) -> Self { pub fn with_bone_idx(mut self, bone_idx: u8) -> Self {
self.bone_idx = bone_idx; self.ao_bone = (self.ao_bone & 0b11) | (bone_idx << 2);
self self
} }
} }

View File

@ -37,6 +37,7 @@ gfx_defines! {
select_pos: [i32; 4] = "select_pos", select_pos: [i32; 4] = "select_pos",
gamma: [f32; 4] = "gamma", gamma: [f32; 4] = "gamma",
cam_mode: u32 = "cam_mode", cam_mode: u32 = "cam_mode",
sprite_render_distance: f32 = "sprite_render_distance",
} }
constant Light { constant Light {
@ -67,6 +68,7 @@ impl Globals {
select_pos: Option<Vec3<i32>>, select_pos: Option<Vec3<i32>>,
gamma: f32, gamma: f32,
cam_mode: CameraMode, cam_mode: CameraMode,
sprite_render_distance: f32,
) -> Self { ) -> Self {
Self { Self {
view_mat: arr_to_mat(view_mat.into_col_array()), view_mat: arr_to_mat(view_mat.into_col_array()),
@ -86,6 +88,7 @@ impl Globals {
.into_array(), .into_array(),
gamma: [gamma; 4], gamma: [gamma; 4],
cam_mode: cam_mode as u32, cam_mode: cam_mode as u32,
sprite_render_distance,
} }
} }
} }
@ -108,6 +111,7 @@ impl Default for Globals {
None, None,
1.0, 1.0,
CameraMode::ThirdPerson, CameraMode::ThirdPerson,
250.0,
) )
} }
} }

View File

@ -12,9 +12,12 @@ use vek::*;
gfx_defines! { gfx_defines! {
vertex Vertex { vertex Vertex {
pos: [f32; 3] = "v_pos", pos: [f32; 3] = "v_pos",
norm: [f32; 3] = "v_norm", // ____BBBBBBBBGGGGGGGGRRRRRRRR
col: [f32; 3] = "v_col", col: u32 = "v_col",
ao: f32 = "v_ao", // ...AANNN
// A = AO
// N = Normal
norm_ao: u32 = "v_norm_ao",
} }
vertex Instance { vertex Instance {
@ -46,11 +49,20 @@ gfx_defines! {
impl Vertex { impl Vertex {
pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>, ao: f32) -> Self { pub fn new(pos: Vec3<f32>, norm: Vec3<f32>, col: Rgb<f32>, ao: f32) -> Self {
let norm_bits = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 }
} else if norm.y != 0.0 {
if norm.y < 0.0 { 2 } else { 3 }
} else {
if norm.z < 0.0 { 4 } else { 5 }
};
Self { Self {
pos: pos.into_array(), pos: pos.into_array(),
col: col.into_array(), col: col
norm: norm.into_array(), .map2(Rgb::new(0, 8, 16), |e, shift| ((e * 255.0) as u32) << shift)
ao, .reduce_bitor(),
norm_ao: norm_bits | (((ao * 3.9999) as u32) << 3),
} }
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -118,17 +118,20 @@ impl FigureMgr {
.map_or(Vec3::zero(), |pos| pos.0); .map_or(Vec3::zero(), |pos| pos.0);
for ( for (
entity, i,
pos, (
interpolated, entity,
vel, pos,
scale, interpolated,
body, vel,
character, scale,
last_character, body,
physics, character,
stats, last_character,
loadout, physics,
stats,
loadout,
),
) in ( ) in (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<Pos>(), &ecs.read_storage::<Pos>(),
@ -143,7 +146,26 @@ impl FigureMgr {
ecs.read_storage::<Loadout>().maybe(), ecs.read_storage::<Loadout>().maybe(),
) )
.join() .join()
.enumerate()
{ {
// Maintaining figure data and sending new figure data to the GPU turns out to
// be a very expensive operation. We want to avoid doing it as much
// as possible, so we make the assumption that players don't care so
// much about the update *rate* for far away things. As the entity
// goes further and further away, we start to 'skip' update ticks.
// TODO: Investigate passing the velocity into the shader so we can at least
// interpolate motion
const MIN_PERFECT_RATE_DIST: f32 = 50.0;
if (i as u64 + tick)
% (1 + ((pos.0.distance_squared(camera.get_focus_pos()).powf(0.25)
- MIN_PERFECT_RATE_DIST.powf(0.5))
.max(0.0)
/ 3.0) as u64)
!= 0
{
continue;
}
let is_player = scene_data.player_entity == entity; let is_player = scene_data.player_entity == entity;
let (pos, ori) = interpolated let (pos, ori) = interpolated
@ -1386,7 +1408,7 @@ impl FigureMgr {
let character_state_storage = state.read_storage::<common::comp::CharacterState>(); let character_state_storage = state.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(player_entity); let character_state = character_state_storage.get(player_entity);
for (entity, _, _, body, _, loadout, _) in ( for (entity, pos, _, body, _, loadout, _) in (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<Pos>(), &ecs.read_storage::<Pos>(),
ecs.read_storage::<Ori>().maybe(), ecs.read_storage::<Ori>().maybe(),
@ -1415,6 +1437,7 @@ impl FigureMgr {
body, body,
loadout, loadout,
false, false,
pos.0,
); );
} }
} }
@ -1437,7 +1460,10 @@ impl FigureMgr {
let character_state_storage = state.read_storage::<common::comp::CharacterState>(); let character_state_storage = state.read_storage::<common::comp::CharacterState>();
let character_state = character_state_storage.get(player_entity); let character_state = character_state_storage.get(player_entity);
if let Some(body) = ecs.read_storage::<Body>().get(player_entity) { if let (Some(pos), Some(body)) = (
ecs.read_storage::<Pos>().get(player_entity),
ecs.read_storage::<Body>().get(player_entity),
) {
let stats_storage = state.read_storage::<Stats>(); let stats_storage = state.read_storage::<Stats>();
let stats = stats_storage.get(player_entity); let stats = stats_storage.get(player_entity);
@ -1461,6 +1487,7 @@ impl FigureMgr {
body, body,
loadout, loadout,
true, true,
pos.0,
); );
} }
} }
@ -1479,6 +1506,7 @@ impl FigureMgr {
body: &Body, body: &Body,
loadout: Option<&Loadout>, loadout: Option<&Loadout>,
is_player: bool, is_player: bool,
pos: Vec3<f32>,
) { ) {
let player_camera_mode = if is_player { let player_camera_mode = if is_player {
camera.get_mode() camera.get_mode()
@ -1691,6 +1719,19 @@ impl FigureMgr {
) )
}), }),
} { } {
const FIGURE_LOW_LOD_DIST: f32 = 150.0;
const FIGURE_MID_LOD_DIST: f32 = 85.0;
let model = if pos.distance_squared(camera.get_focus_pos())
> FIGURE_LOW_LOD_DIST.powf(2.0)
{
&model[2]
} else if pos.distance_squared(camera.get_focus_pos()) > FIGURE_MID_LOD_DIST.powf(2.0) {
&model[1]
} else {
&model[0]
};
if is_player { if is_player {
renderer.render_player( renderer.render_player(
model, model,
@ -1850,7 +1891,10 @@ impl<S: Skeleton> FigureState<S> {
renderer.update_consts(&mut self.locals, &[locals]).unwrap(); renderer.update_consts(&mut self.locals, &[locals]).unwrap();
renderer renderer
.update_consts(&mut self.bone_consts, &self.skeleton.compute_matrices()) .update_consts(
&mut self.bone_consts,
&self.skeleton.compute_matrices()[0..self.skeleton.bone_count()],
)
.unwrap(); .unwrap();
} }

View File

@ -85,6 +85,7 @@ pub struct SceneData<'a> {
pub thread_pool: &'a uvth::ThreadPool, pub thread_pool: &'a uvth::ThreadPool,
pub gamma: f32, pub gamma: f32,
pub mouse_smoothing: bool, pub mouse_smoothing: bool,
pub sprite_render_distance: f32,
} }
impl Scene { impl Scene {
@ -365,6 +366,7 @@ impl Scene {
self.select_pos, self.select_pos,
scene_data.gamma, scene_data.gamma,
self.camera.get_mode(), self.camera.get_mode(),
scene_data.sprite_render_distance as f32 - 20.0,
)]) )])
.expect("Failed to update global constants"); .expect("Failed to update global constants");
@ -401,6 +403,7 @@ impl Scene {
state: &State, state: &State,
player_entity: EcsEntity, player_entity: EcsEntity,
tick: u64, tick: u64,
scene_data: &SceneData,
) { ) {
// Render terrain and figures. // Render terrain and figures.
self.terrain.render( self.terrain.render(
@ -446,6 +449,7 @@ impl Scene {
&self.shadows, &self.shadows,
&self.lod, &self.lod,
self.camera.get_focus_pos(), self.camera.get_focus_pos(),
scene_data.sprite_render_distance,
); );
renderer.render_post_process( renderer.render_post_process(

View File

@ -4,8 +4,9 @@ use crate::{
fixture::FixtureSkeleton, fixture::FixtureSkeleton,
Animation, Skeleton, Animation, Skeleton,
}, },
mesh::Meshable,
render::{ render::{
create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Model, create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Mesh, Model,
PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
}, },
scene::{ scene::{
@ -19,6 +20,7 @@ use crate::{
use client::Client; use client::Client;
use common::{ use common::{
comp::{humanoid, Body, Loadout}, comp::{humanoid, Body, Loadout},
figure::Segment,
terrain::BlockKind, terrain::BlockKind,
vol::{BaseVol, ReadVol, Vox}, vol::{BaseVol, ReadVol, Vox},
}; };
@ -43,6 +45,10 @@ impl ReadVol for VoidVol {
fn get<'a>(&'a self, _pos: Vec3<i32>) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) } fn get<'a>(&'a self, _pos: Vec3<i32>) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) }
} }
fn generate_mesh(segment: &Segment, offset: Vec3<f32>) -> Mesh<FigurePipeline> {
Meshable::<FigurePipeline, FigurePipeline>::generate_mesh(segment, (offset, Vec3::one())).0
}
struct Skybox { struct Skybox {
model: Model<SkyboxPipeline>, model: Model<SkyboxPipeline>,
locals: Consts<SkyboxLocals>, locals: Consts<SkyboxLocals>,
@ -120,7 +126,11 @@ impl Scene {
backdrop: backdrop.map(|specifier| { backdrop: backdrop.map(|specifier| {
( (
renderer renderer
.create_model(&load_mesh(specifier, Vec3::new(-55.0, -49.5, -2.0))) .create_model(&load_mesh(
specifier,
Vec3::new(-55.0, -49.5, -2.0),
generate_mesh,
))
.unwrap(), .unwrap(),
FigureState::new(renderer, FixtureSkeleton::new()), FigureState::new(renderer, FixtureSkeleton::new()),
) )
@ -188,6 +198,7 @@ impl Scene {
None, None,
scene_data.gamma, scene_data.gamma,
self.camera.get_mode(), self.camera.get_mode(),
250.0,
)]) { )]) {
error!("Renderer failed to update: {:?}", err); error!("Renderer failed to update: {:?}", err);
} }
@ -244,7 +255,7 @@ impl Scene {
.0; .0;
renderer.render_figure( renderer.render_figure(
model, &model[0],
&self.globals, &self.globals,
self.figure_state.locals(), self.figure_state.locals(),
self.figure_state.bone_consts(), self.figure_state.bone_consts(),

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@ use crate::{
i18n::{i18n_asset_key, VoxygenLocalization}, i18n::{i18n_asset_key, VoxygenLocalization},
key_state::KeyState, key_state::KeyState,
menu::char_selection::CharSelectionState, menu::char_selection::CharSelectionState,
render::Renderer,
scene::{camera, Scene, SceneData}, scene::{camera, Scene, SceneData},
settings::AudioOutput, settings::AudioOutput,
window::{AnalogGameInput, Event, GameInput}, window::{AnalogGameInput, Event, GameInput},
@ -115,26 +114,6 @@ impl SessionState {
/// Clean up the session (and the client attached to it) after a tick. /// Clean up the session (and the client attached to it) after a tick.
pub fn cleanup(&mut self) { self.client.borrow_mut().cleanup(); } pub fn cleanup(&mut self) { self.client.borrow_mut().cleanup(); }
/// Render the session to the screen.
///
/// This method should be called once per frame.
pub fn render(&mut self, renderer: &mut Renderer) {
// Clear the screen
renderer.clear();
// Render the screen using the global renderer
{
let client = self.client.borrow();
self.scene
.render(renderer, client.state(), client.entity(), client.get_tick());
}
// Draw the UI to the screen
self.hud.render(renderer, self.scene.globals());
// Finish the frame
renderer.flush();
}
} }
impl PlayState for SessionState { impl PlayState for SessionState {
@ -599,6 +578,11 @@ impl PlayState for SessionState {
global_state.settings.graphics.lod_detail = lod_detail; global_state.settings.graphics.lod_detail = lod_detail;
global_state.settings.save_to_file_warn(); global_state.settings.save_to_file_warn();
}, },
HudEvent::AdjustSpriteRenderDistance(sprite_render_distance) => {
global_state.settings.graphics.sprite_render_distance =
sprite_render_distance;
global_state.settings.save_to_file_warn();
},
HudEvent::CrosshairTransp(crosshair_transp) => { HudEvent::CrosshairTransp(crosshair_transp) => {
global_state.settings.gameplay.crosshair_transp = crosshair_transp; global_state.settings.gameplay.crosshair_transp = crosshair_transp;
global_state.settings.save_to_file_warn(); global_state.settings.save_to_file_warn();
@ -730,9 +714,6 @@ impl PlayState for SessionState {
} }
} }
// Runs if either in a multiplayer server or the singleplayer server is unpaused
if global_state.singleplayer.is_none()
|| !global_state.singleplayer.as_ref().unwrap().is_paused()
{ {
let client = self.client.borrow(); let client = self.client.borrow();
let scene_data = SceneData { let scene_data = SceneData {
@ -744,17 +725,38 @@ impl PlayState for SessionState {
thread_pool: client.thread_pool(), thread_pool: client.thread_pool(),
gamma: global_state.settings.graphics.gamma, gamma: global_state.settings.graphics.gamma,
mouse_smoothing: global_state.settings.gameplay.smooth_pan_enable, mouse_smoothing: global_state.settings.gameplay.smooth_pan_enable,
sprite_render_distance: global_state.settings.graphics.sprite_render_distance
as f32,
}; };
self.scene.maintain(
global_state.window.renderer_mut(), // Runs if either in a multiplayer server or the singleplayer server is unpaused
&mut global_state.audio, if global_state.singleplayer.is_none()
|| !global_state.singleplayer.as_ref().unwrap().is_paused()
{
self.scene.maintain(
global_state.window.renderer_mut(),
&mut global_state.audio,
&scene_data,
);
}
let renderer = global_state.window.renderer_mut();
// Clear the screen
renderer.clear();
// Render the screen using the global renderer
self.scene.render(
renderer,
client.state(),
client.entity(),
client.get_tick(),
&scene_data, &scene_data,
); );
// Draw the UI to the screen
self.hud.render(renderer, self.scene.globals());
// Finish the frame
renderer.flush();
} }
// Render the session.
self.render(global_state.window.renderer_mut());
// Display the frame on the window. // Display the frame on the window.
global_state global_state
.window .window

View File

@ -548,6 +548,7 @@ impl Default for Log {
#[serde(default)] #[serde(default)]
pub struct GraphicsSettings { pub struct GraphicsSettings {
pub view_distance: u32, pub view_distance: u32,
pub sprite_render_distance: u32,
pub max_fps: u32, pub max_fps: u32,
pub fov: u16, pub fov: u16,
pub gamma: f32, pub gamma: f32,
@ -563,6 +564,7 @@ impl Default for GraphicsSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
view_distance: 10, view_distance: 10,
sprite_render_distance: 250,
max_fps: 60, max_fps: 60,
fov: 50, fov: 50,
gamma: 1.0, gamma: 1.0,

View File

@ -140,12 +140,13 @@ impl Civs {
e * sz as i32 + sz as i32 / 2 e * sz as i32 + sz as i32 / 2
}); });
let mut rng = ctx.reseed().rng;
let world_site = match &site.kind { let world_site = match &site.kind {
SiteKind::Settlement => { SiteKind::Settlement => {
WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), &mut ctx.rng)) WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), &mut rng))
}, },
SiteKind::Dungeon => { SiteKind::Dungeon => {
WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), &mut ctx.rng)) WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), &mut rng))
}, },
}; };

View File

@ -116,7 +116,7 @@ impl Archetype for House {
storey_fill: StoreyFill::All, storey_fill: StoreyFill::All,
mansard: 0, mansard: 0,
pillar: match rng.gen_range(0, 3) { pillar: match rng.gen_range(0, 3) {
0 => Pillar::Chimney(9 + locus + rng.gen_range(0, 4)), 0 => Pillar::Chimney(10 + locus + rng.gen_range(0, 4)),
1 => Pillar::Tower(15 + locus + rng.gen_range(0, 4)), 1 => Pillar::Tower(15 + locus + rng.gen_range(0, 4)),
_ => Pillar::None, _ => Pillar::None,
}, },
@ -199,7 +199,8 @@ impl Archetype for House {
let facade_layer = 3; let facade_layer = 3;
let structural_layer = facade_layer + 1; let structural_layer = facade_layer + 1;
let foundation_layer = structural_layer + 1; let internal_layer = structural_layer + 1;
let foundation_layer = internal_layer + 1;
let floor_layer = foundation_layer + 1; let floor_layer = foundation_layer + 1;
let foundation = make_block(100, 100, 100).with_priority(foundation_layer); let foundation = make_block(100, 100, 100).with_priority(foundation_layer);
@ -207,9 +208,9 @@ impl Archetype for House {
let floor = make_block(100, 75, 50); let floor = make_block(100, 75, 50);
let wall = make_block(200, 180, 150).with_priority(facade_layer); let wall = make_block(200, 180, 150).with_priority(facade_layer);
let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b) let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b)
.with_priority(facade_layer); .with_priority(facade_layer - 1);
let empty = BlockMask::nothing(); let empty = BlockMask::nothing();
let internal = BlockMask::new(Block::empty(), structural_layer); let internal = BlockMask::new(Block::empty(), internal_layer);
let end_window = BlockMask::new( let end_window = BlockMask::new(
Block::new(BlockKind::Window1, make_meta(ori.flip())), Block::new(BlockKind::Window1, make_meta(ori.flip())),
structural_layer, structural_layer,