Initial entity work, moved self handling to an entity
This commit is contained in:
parent
7f1e859a87
commit
80f2121bf7
|
@ -29,11 +29,19 @@ pub struct Entity {
|
||||||
|
|
||||||
/// Used to access compoents on an entity in an efficient
|
/// Used to access compoents on an entity in an efficient
|
||||||
/// way.
|
/// way.
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct Key<T> {
|
pub struct Key<T> {
|
||||||
id: usize,
|
id: usize,
|
||||||
_t: PhantomData<T>,
|
_t: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
impl <T> Clone for Key<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Key {
|
||||||
|
id: self.id,
|
||||||
|
_t: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl <T> Copy for Key<T> {}
|
||||||
|
|
||||||
/// Used to search for entities with the requested components.
|
/// Used to search for entities with the requested components.
|
||||||
pub struct Filter {
|
pub struct Filter {
|
||||||
|
@ -73,8 +81,8 @@ pub struct Manager {
|
||||||
|
|
||||||
component_ids: RefCell<HashMap<TypeId, usize>>,
|
component_ids: RefCell<HashMap<TypeId, usize>>,
|
||||||
|
|
||||||
systems: Vec<Box<System>>,
|
systems: Vec<Box<System + Send>>,
|
||||||
render_systems: Vec<Box<System>>,
|
render_systems: Vec<Box<System + Send>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Manager {
|
impl Manager {
|
||||||
|
@ -101,12 +109,12 @@ impl Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a system which will be called every tick
|
/// Adds a system which will be called every tick
|
||||||
pub fn add_system<S: System + 'static>(&mut self, s: S) {
|
pub fn add_system<S: System + Send + 'static>(&mut self, s: S) {
|
||||||
self.systems.push(Box::new(s));
|
self.systems.push(Box::new(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a system which will be called every frame
|
/// Adds a system which will be called every frame
|
||||||
pub fn add_render_system<S: System + 'static>(&mut self, s: S) {
|
pub fn add_render_system<S: System + Send + 'static>(&mut self, s: S) {
|
||||||
self.render_systems.push(Box::new(s));
|
self.render_systems.push(Box::new(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,8 +250,44 @@ impl Manager {
|
||||||
self.add_component(entity, key, val);
|
self.add_component(entity, key, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the component to the target entity. Returns whether anything
|
||||||
|
/// was removed.
|
||||||
|
/// # Panics
|
||||||
|
/// Panics when the target entity doesn't exist
|
||||||
|
pub fn remove_component<T>(&mut self, entity: Entity, key: Key<T>) -> bool {
|
||||||
|
if self.components.len() <= key.id {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if self.components[key.id].is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let components = self.components.get_mut(key.id).and_then(|v| v.as_mut()).unwrap();
|
||||||
|
let mut e = self.entities.get_mut(entity.id);
|
||||||
|
let set = match e {
|
||||||
|
Some(ref mut val) => if val.1 == entity.generation { &mut val.0 } else { panic!("Missing entity") },
|
||||||
|
None => panic!("Missing entity"),
|
||||||
|
};
|
||||||
|
let set = match set.as_mut() {
|
||||||
|
Some(val) => val,
|
||||||
|
None => panic!("Missing entity"),
|
||||||
|
};
|
||||||
|
if !set.get(key.id) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
set.set(key.id, false);
|
||||||
|
components.remove(entity.id);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `remove_component` but doesn't require a key. Using a key
|
||||||
|
/// is better for frequent lookups.
|
||||||
|
pub fn remove_component_direct<T: Any>(&mut self, entity: Entity) -> bool {
|
||||||
|
let key = self.get_key();
|
||||||
|
self.remove_component::<T>(entity, key)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the given component that the key points to if it exists.
|
/// Returns the given component that the key points to if it exists.
|
||||||
pub fn get_component<T>(&self, entity: Entity, key: Key<T>) -> Option<&T> {
|
pub fn get_component<'a, 'b, T>(&'a self, entity: Entity, key: Key<T>) -> Option<&'b T> {
|
||||||
let components = match self.components.get(key.id).and_then(|v| v.as_ref()) {
|
let components = match self.components.get(key.id).and_then(|v| v.as_ref()) {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return None,
|
None => return None,
|
||||||
|
@ -256,18 +300,18 @@ impl Manager {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(components.get(entity.id))
|
Some(unsafe { mem::transmute::<&T, &T>(components.get(entity.id)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `get_component` but doesn't require a key. Using a key
|
/// Same as `get_component` but doesn't require a key. Using a key
|
||||||
/// is better for frequent lookups.
|
/// is better for frequent lookups.
|
||||||
pub fn get_component_direct<T: Any>(&self, entity: Entity) -> Option<&T> {
|
pub fn get_component_direct<'a, 'b, T: Any>(&'a self, entity: Entity) -> Option<&'b T> {
|
||||||
let key = self.get_key();
|
let key = self.get_key();
|
||||||
self.get_component(entity, key)
|
self.get_component(entity, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the given component that the key points to if it exists.
|
/// Returns the given component that the key points to if it exists.
|
||||||
pub fn get_component_mut<T>(&mut self, entity: Entity, key: Key<T>) -> Option<&mut T> {
|
pub fn get_component_mut<'a, 'b, T>(&'a mut self, entity: Entity, key: Key<T>) -> Option<&'b mut T> {
|
||||||
let components = match self.components.get_mut(key.id).and_then(|v| v.as_mut()) {
|
let components = match self.components.get_mut(key.id).and_then(|v| v.as_mut()) {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return None,
|
None => return None,
|
||||||
|
@ -280,12 +324,12 @@ impl Manager {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(components.get_mut(entity.id))
|
Some(unsafe { mem::transmute::<&mut T, &mut T>(components.get_mut(entity.id)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `get_component_mut` but doesn't require a key. Using a key
|
/// Same as `get_component_mut` but doesn't require a key. Using a key
|
||||||
/// is better for frequent lookups.
|
/// is better for frequent lookups.
|
||||||
pub fn get_component_mut_direct<T: Any>(&mut self, entity: Entity) -> Option<&mut T> {
|
pub fn get_component_mut_direct<'a, 'b, T: Any>(&'a mut self, entity: Entity) -> Option<&'b mut T> {
|
||||||
let key = self.get_key();
|
let key = self.get_key();
|
||||||
self.get_component_mut(entity, key)
|
self.get_component_mut(entity, key)
|
||||||
}
|
}
|
||||||
|
@ -296,7 +340,7 @@ const COMPONENTS_PER_BLOCK: usize = 64;
|
||||||
struct ComponentMem {
|
struct ComponentMem {
|
||||||
data: Vec<Option<(Vec<u8>, BSet, usize)>>,
|
data: Vec<Option<(Vec<u8>, BSet, usize)>>,
|
||||||
component_size: usize,
|
component_size: usize,
|
||||||
drop_func: Box<Fn(*mut u8)>,
|
drop_func: Box<Fn(*mut u8) + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentMem {
|
impl ComponentMem {
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
|
||||||
|
pub mod player;
|
||||||
|
|
||||||
|
use ecs;
|
||||||
|
use cgmath::Vector3;
|
||||||
|
use collision::Aabb3;
|
||||||
|
|
||||||
|
mod systems;
|
||||||
|
|
||||||
|
pub fn add_systems(m: &mut ecs::Manager) {
|
||||||
|
let sys = systems::UpdateLastPosition::new(m);
|
||||||
|
m.add_system(sys);
|
||||||
|
|
||||||
|
player::add_systems(m);
|
||||||
|
|
||||||
|
let sys = systems::ApplyVelocity::new(m);
|
||||||
|
m.add_system(sys);
|
||||||
|
let sys = systems::ApplyGravity::new(m);
|
||||||
|
m.add_system(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Location of an entity in the world.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Position {
|
||||||
|
pub position: Vector3<f64>,
|
||||||
|
pub last_position: Vector3<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn new(x: f64, y: f64, z: f64) -> Position {
|
||||||
|
Position {
|
||||||
|
position: Vector3::new(x, y, z),
|
||||||
|
last_position: Vector3::new(x, y, z),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero() -> Position {
|
||||||
|
Position::new(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Velocity of an entity in the world.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Velocity {
|
||||||
|
pub velocity: Vector3<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Velocity {
|
||||||
|
pub fn new(x: f64, y: f64, z: f64) -> Velocity {
|
||||||
|
Velocity {
|
||||||
|
velocity: Vector3::new(x, y, z),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero() -> Velocity {
|
||||||
|
Velocity::new(0.0, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rotation of an entity in the world
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Rotation {
|
||||||
|
pub yaw: f64,
|
||||||
|
pub pitch: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rotation {
|
||||||
|
pub fn new(yaw: f64, pitch: f64) -> Rotation {
|
||||||
|
Rotation {
|
||||||
|
yaw: yaw,
|
||||||
|
pitch: pitch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zero() -> Rotation {
|
||||||
|
Rotation::new(0.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Gravity {
|
||||||
|
pub on_ground: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gravity {
|
||||||
|
pub fn new() -> Gravity {
|
||||||
|
Gravity {
|
||||||
|
on_ground: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bounds {
|
||||||
|
pub bounds: Aabb3<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bounds {
|
||||||
|
pub fn new(bounds: Aabb3<f64>) -> Bounds {
|
||||||
|
Bounds {
|
||||||
|
bounds: bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hack to allow temp access to a type
|
||||||
|
pub struct Proxy<T> {
|
||||||
|
inner: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T> ::std::ops::Deref for Proxy<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
self.inner.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T> Proxy<T> {
|
||||||
|
|
||||||
|
pub fn new() -> Proxy<T> {
|
||||||
|
Proxy {
|
||||||
|
inner: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn give(&mut self, v: &mut T) {
|
||||||
|
use std::mem;
|
||||||
|
self.inner = Some(mem::replace(v, unsafe { mem::uninitialized() }));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self, v: &mut T) {
|
||||||
|
use std::mem;
|
||||||
|
mem::forget(mem::replace(v, self.inner.take().unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GameInfo {
|
||||||
|
pub delta: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameInfo {
|
||||||
|
pub fn new() -> GameInfo {
|
||||||
|
GameInfo {
|
||||||
|
delta: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,338 @@
|
||||||
|
|
||||||
|
use ecs;
|
||||||
|
use super::{
|
||||||
|
Position,
|
||||||
|
Velocity,
|
||||||
|
Rotation,
|
||||||
|
Gravity,
|
||||||
|
Bounds,
|
||||||
|
Proxy,
|
||||||
|
GameInfo,
|
||||||
|
};
|
||||||
|
use world;
|
||||||
|
use types::Gamemode;
|
||||||
|
use collision::{Aabb, Aabb3};
|
||||||
|
use cgmath::{self, Point3};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::BuildHasherDefault;
|
||||||
|
use types::hash::FNVHash;
|
||||||
|
use sdl2::keyboard::Keycode;
|
||||||
|
|
||||||
|
pub fn add_systems(m: &mut ecs::Manager) {
|
||||||
|
// Not actually rendering related but the faster
|
||||||
|
// we can handle input the better.
|
||||||
|
let sys = MovementHandler::new(m);
|
||||||
|
m.add_render_system(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_local(m: &mut ecs::Manager) -> ecs::Entity {
|
||||||
|
let entity = m.create_entity();
|
||||||
|
m.add_component_direct(entity, Position::new(0.5, 13.2, 0.5));
|
||||||
|
m.add_component_direct(entity, Rotation::new(0.0, 0.0));
|
||||||
|
m.add_component_direct(entity, Velocity::new(0.0, 0.0, 0.0));
|
||||||
|
m.add_component_direct(entity, Gamemode::Survival);
|
||||||
|
m.add_component_direct(entity, Gravity::new());
|
||||||
|
m.add_component_direct(entity, PlayerMovement::new());
|
||||||
|
m.add_component_direct(entity, Bounds::new(Aabb3::new(
|
||||||
|
Point3::new(-0.3, 0.0, -0.3),
|
||||||
|
Point3::new(0.3, 1.8, 0.3)
|
||||||
|
)));
|
||||||
|
entity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PlayerMovement {
|
||||||
|
pub flying: bool,
|
||||||
|
pub did_touch_ground: bool,
|
||||||
|
pub pressed_keys: HashMap<Keycode, bool, BuildHasherDefault<FNVHash>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerMovement {
|
||||||
|
pub fn new() -> PlayerMovement {
|
||||||
|
PlayerMovement {
|
||||||
|
flying: false,
|
||||||
|
did_touch_ground: false,
|
||||||
|
pressed_keys: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_movement(&self, player_yaw: f64) -> (f64, f64) {
|
||||||
|
use std::f64::consts::PI;
|
||||||
|
let mut forward = 0.0f64;
|
||||||
|
let mut yaw = player_yaw - (PI/2.0);
|
||||||
|
if self.is_key_pressed(Keycode::W) || self.is_key_pressed(Keycode::S) {
|
||||||
|
forward = 1.0;
|
||||||
|
if self.is_key_pressed(Keycode::S) {
|
||||||
|
yaw += PI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut change = 0.0;
|
||||||
|
if self.is_key_pressed(Keycode::A) {
|
||||||
|
change = (PI / 2.0) / (forward.abs() + 1.0);
|
||||||
|
}
|
||||||
|
if self.is_key_pressed(Keycode::D) {
|
||||||
|
change = -(PI / 2.0) / (forward.abs() + 1.0);
|
||||||
|
}
|
||||||
|
if self.is_key_pressed(Keycode::A) || self.is_key_pressed(Keycode::D) {
|
||||||
|
forward = 1.0;
|
||||||
|
}
|
||||||
|
if self.is_key_pressed(Keycode::S) {
|
||||||
|
yaw -= change;
|
||||||
|
} else {
|
||||||
|
yaw += change;
|
||||||
|
}
|
||||||
|
|
||||||
|
(forward, yaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_key_pressed(&self, key: Keycode) -> bool {
|
||||||
|
self.pressed_keys.get(&key).map_or(false, |v| *v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MovementHandler {
|
||||||
|
filter: ecs::Filter,
|
||||||
|
movement: ecs::Key<PlayerMovement>,
|
||||||
|
gravity: ecs::Key<Gravity>,
|
||||||
|
gamemode: ecs::Key<Gamemode>,
|
||||||
|
world: ecs::Key<Proxy<world::World>>,
|
||||||
|
position: ecs::Key<Position>,
|
||||||
|
velocity: ecs::Key<Velocity>,
|
||||||
|
game_info: ecs::Key<GameInfo>,
|
||||||
|
bounds: ecs::Key<Bounds>,
|
||||||
|
rotation: ecs::Key<Rotation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MovementHandler {
|
||||||
|
pub fn new(m: &mut ecs::Manager) -> MovementHandler {
|
||||||
|
let movement = m.get_key();
|
||||||
|
let position = m.get_key();
|
||||||
|
let velocity = m.get_key();
|
||||||
|
let bounds = m.get_key();
|
||||||
|
let rotation = m.get_key();
|
||||||
|
MovementHandler {
|
||||||
|
filter: ecs::Filter::new()
|
||||||
|
.with(movement)
|
||||||
|
.with(position)
|
||||||
|
.with(velocity)
|
||||||
|
.with(bounds)
|
||||||
|
.with(rotation),
|
||||||
|
movement: movement,
|
||||||
|
gravity: m.get_key(),
|
||||||
|
gamemode: m.get_key(),
|
||||||
|
world: m.get_key(),
|
||||||
|
position: position,
|
||||||
|
velocity: velocity,
|
||||||
|
game_info: m.get_key(),
|
||||||
|
bounds: bounds,
|
||||||
|
rotation: rotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ecs::System for MovementHandler {
|
||||||
|
fn update(&mut self, m: &mut ecs::Manager) {
|
||||||
|
let world_entity = m.get_world();
|
||||||
|
let world: &world::World = m.get_component(world_entity, self.world).unwrap();
|
||||||
|
let delta = m.get_component(world_entity, self.game_info).unwrap().delta;
|
||||||
|
for e in m.find(&self.filter) {
|
||||||
|
let movement = m.get_component_mut(e, self.movement).unwrap();
|
||||||
|
if movement.flying && m.get_component(e, self.gravity).is_some() {
|
||||||
|
m.remove_component(e, self.gravity);
|
||||||
|
} else if !movement.flying && m.get_component(e, self.gravity).is_none() {
|
||||||
|
m.add_component(e, self.gravity, Gravity::new());
|
||||||
|
}
|
||||||
|
let gamemode = m.get_component(e, self.gamemode).unwrap();
|
||||||
|
movement.flying |= gamemode.always_fly();
|
||||||
|
|
||||||
|
let position = m.get_component_mut(e, self.position).unwrap();
|
||||||
|
let rotation = m.get_component(e, self.rotation).unwrap();
|
||||||
|
let velocity = m.get_component_mut(e, self.velocity).unwrap();
|
||||||
|
let gravity = m.get_component_mut(e, self.gravity);
|
||||||
|
|
||||||
|
let player_bounds = m.get_component(e, self.bounds).unwrap().bounds;
|
||||||
|
|
||||||
|
if world.is_chunk_loaded((position.position.x as i32) >> 4, (position.position.z as i32) >> 4) {
|
||||||
|
let (forward, yaw) = movement.calculate_movement(rotation.yaw);
|
||||||
|
let mut speed = 4.317 / 60.0;
|
||||||
|
if movement.is_key_pressed(Keycode::LShift) {
|
||||||
|
speed = 5.612 / 60.0;
|
||||||
|
}
|
||||||
|
if movement.flying {
|
||||||
|
speed *= 2.5;
|
||||||
|
|
||||||
|
if movement.is_key_pressed(Keycode::Space) {
|
||||||
|
position.position.y += speed * delta;
|
||||||
|
}
|
||||||
|
if movement.is_key_pressed(Keycode::LCtrl) {
|
||||||
|
position.position.y -= speed * delta;
|
||||||
|
}
|
||||||
|
} else if gravity.as_ref().map_or(false, |v| v.on_ground) {
|
||||||
|
if movement.is_key_pressed(Keycode::Space) {
|
||||||
|
velocity.velocity.y = 0.15;
|
||||||
|
} else {
|
||||||
|
velocity.velocity.y = 0.0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
velocity.velocity.y -= 0.01 * delta;
|
||||||
|
if velocity.velocity.y < -0.3 {
|
||||||
|
velocity.velocity.y = -0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position.position.x += forward * yaw.cos() * delta * speed;
|
||||||
|
position.position.z -= forward * yaw.sin() * delta * speed;
|
||||||
|
position.position.y += velocity.velocity.y * delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gamemode.noclip() {
|
||||||
|
let mut target = position.position;
|
||||||
|
position.position.y = position.last_position.y;
|
||||||
|
position.position.z = position.last_position.z;
|
||||||
|
|
||||||
|
// We handle each axis separately to allow for a sliding
|
||||||
|
// effect when pushing up against walls.
|
||||||
|
|
||||||
|
let (bounds, xhit) = check_collisions(world, position, player_bounds);
|
||||||
|
position.position.x = bounds.min.x + 0.3;
|
||||||
|
position.last_position.x = position.position.x;
|
||||||
|
|
||||||
|
position.position.z = target.z;
|
||||||
|
let (bounds, zhit) = check_collisions(world, position, player_bounds);
|
||||||
|
position.position.z = bounds.min.z + 0.3;
|
||||||
|
position.last_position.z = position.position.z;
|
||||||
|
|
||||||
|
// Half block jumps
|
||||||
|
// Minecraft lets you 'jump' up 0.5 blocks
|
||||||
|
// for slabs and stairs (or smaller blocks).
|
||||||
|
// Currently we implement this as a teleport to the
|
||||||
|
// top of the block if we could move there
|
||||||
|
// but this isn't smooth.
|
||||||
|
if (xhit || zhit) && gravity.as_ref().map_or(false, |v| v.on_ground) {
|
||||||
|
let mut ox = position.position.x;
|
||||||
|
let mut oz = position.position.z;
|
||||||
|
position.position.x = target.x;
|
||||||
|
position.position.z = target.z;
|
||||||
|
for offset in 1 .. 9 {
|
||||||
|
let mini = player_bounds.add_v(cgmath::Vector3::new(0.0, offset as f64 / 16.0, 0.0));
|
||||||
|
let (_, hit) = check_collisions(world, position, mini);
|
||||||
|
if !hit {
|
||||||
|
target.y += offset as f64 / 16.0;
|
||||||
|
ox = target.x;
|
||||||
|
oz = target.z;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position.position.x = ox;
|
||||||
|
position.position.z = oz;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.position.y = target.y;
|
||||||
|
let (bounds, yhit) = check_collisions(world, position, player_bounds);
|
||||||
|
position.position.y = bounds.min.y;
|
||||||
|
position.last_position.y = position.position.y;
|
||||||
|
if yhit {
|
||||||
|
velocity.velocity.y = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(gravity) = gravity {
|
||||||
|
let ground = Aabb3::new(
|
||||||
|
Point3::new(-0.3, -0.05, -0.3),
|
||||||
|
Point3::new(0.3, 0.0, 0.3)
|
||||||
|
);
|
||||||
|
let prev = gravity.on_ground;
|
||||||
|
let (_, hit) = check_collisions(world, position, ground);
|
||||||
|
gravity.on_ground = hit;
|
||||||
|
if !prev && gravity.on_ground {
|
||||||
|
movement.did_touch_ground = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn check_collisions(world: &world::World, position: &mut Position, bounds: Aabb3<f64>) -> (Aabb3<f64>, bool) {
|
||||||
|
let mut bounds = bounds.add_v(position.position);
|
||||||
|
|
||||||
|
let dir = position.position - position.last_position;
|
||||||
|
|
||||||
|
let min_x = (bounds.min.x - 1.0) as i32;
|
||||||
|
let min_y = (bounds.min.y - 1.0) as i32;
|
||||||
|
let min_z = (bounds.min.z - 1.0) as i32;
|
||||||
|
let max_x = (bounds.max.x + 1.0) as i32;
|
||||||
|
let max_y = (bounds.max.y + 1.0) as i32;
|
||||||
|
let max_z = (bounds.max.z + 1.0) as i32;
|
||||||
|
|
||||||
|
let mut hit = false;
|
||||||
|
for y in min_y .. max_y {
|
||||||
|
for z in min_z .. max_z {
|
||||||
|
for x in min_x .. max_x {
|
||||||
|
let block = world.get_block(x, y, z);
|
||||||
|
for bb in block.get_collision_boxes() {
|
||||||
|
let bb = bb.add_v(cgmath::Vector3::new(x as f64, y as f64, z as f64));
|
||||||
|
if bb.collides(&bounds) {
|
||||||
|
bounds = bounds.move_out_of(bb, dir);
|
||||||
|
hit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(bounds, hit)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Collidable<T> {
|
||||||
|
fn collides(&self, t: &T) -> bool;
|
||||||
|
fn move_out_of(self, other: Self, dir: cgmath::Vector3<f64>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collidable<Aabb3<f64>> for Aabb3<f64> {
|
||||||
|
fn collides(&self, t: &Aabb3<f64>) -> bool {
|
||||||
|
!(
|
||||||
|
t.min.x >= self.max.x ||
|
||||||
|
t.max.x <= self.min.x ||
|
||||||
|
t.min.y >= self.max.y ||
|
||||||
|
t.max.y <= self.min.y ||
|
||||||
|
t.min.z >= self.max.z ||
|
||||||
|
t.max.z <= self.min.z
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_out_of(mut self, other: Self, dir: cgmath::Vector3<f64>) -> Self {
|
||||||
|
if dir.x != 0.0 {
|
||||||
|
if dir.x > 0.0 {
|
||||||
|
let ox = self.max.x;
|
||||||
|
self.max.x = other.min.x - 0.0001;
|
||||||
|
self.min.x += self.max.x - ox;
|
||||||
|
} else {
|
||||||
|
let ox = self.min.x;
|
||||||
|
self.min.x = other.max.x + 0.0001;
|
||||||
|
self.max.x += self.min.x - ox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dir.y != 0.0 {
|
||||||
|
if dir.y > 0.0 {
|
||||||
|
let oy = self.max.y;
|
||||||
|
self.max.y = other.min.y - 0.0001;
|
||||||
|
self.min.y += self.max.y - oy;
|
||||||
|
} else {
|
||||||
|
let oy = self.min.y;
|
||||||
|
self.min.y = other.max.y + 0.0001;
|
||||||
|
self.max.y += self.min.y - oy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dir.z != 0.0 {
|
||||||
|
if dir.z > 0.0 {
|
||||||
|
let oz = self.max.z;
|
||||||
|
self.max.z = other.min.z - 0.0001;
|
||||||
|
self.min.z += self.max.z - oz;
|
||||||
|
} else {
|
||||||
|
let oz = self.min.z;
|
||||||
|
self.min.z = other.max.z + 0.0001;
|
||||||
|
self.max.z += self.min.z - oz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use ecs;
|
||||||
|
|
||||||
|
pub struct ApplyVelocity {
|
||||||
|
filter: ecs::Filter,
|
||||||
|
position: ecs::Key<Position>,
|
||||||
|
velocity: ecs::Key<Velocity>,
|
||||||
|
movement: ecs::Key<super::player::PlayerMovement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplyVelocity {
|
||||||
|
pub fn new(m: &mut ecs::Manager) -> ApplyVelocity {
|
||||||
|
let position = m.get_key();
|
||||||
|
let velocity = m.get_key();
|
||||||
|
ApplyVelocity {
|
||||||
|
filter: ecs::Filter::new()
|
||||||
|
.with(position)
|
||||||
|
.with(velocity),
|
||||||
|
position: position,
|
||||||
|
velocity: velocity,
|
||||||
|
movement: m.get_key(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ecs::System for ApplyVelocity {
|
||||||
|
|
||||||
|
fn update(&mut self, m: &mut ecs::Manager) {
|
||||||
|
for e in m.find(&self.filter) {
|
||||||
|
if m.get_component(e, self.movement).is_some() {
|
||||||
|
// Player's handle their own phyiscs
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let pos = m.get_component_mut(e, self.position).unwrap();
|
||||||
|
let vel = m.get_component(e, self.velocity).unwrap();
|
||||||
|
pos.position = pos.position + vel.velocity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ApplyGravity {
|
||||||
|
filter: ecs::Filter,
|
||||||
|
velocity: ecs::Key<Velocity>,
|
||||||
|
movement: ecs::Key<super::player::PlayerMovement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplyGravity {
|
||||||
|
pub fn new(m: &mut ecs::Manager) -> ApplyGravity {
|
||||||
|
let gravity = m.get_key::<Gravity>();
|
||||||
|
let velocity = m.get_key();
|
||||||
|
ApplyGravity {
|
||||||
|
filter: ecs::Filter::new()
|
||||||
|
.with(gravity)
|
||||||
|
.with(velocity),
|
||||||
|
velocity: velocity,
|
||||||
|
movement: m.get_key(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ecs::System for ApplyGravity {
|
||||||
|
|
||||||
|
fn update(&mut self, m: &mut ecs::Manager) {
|
||||||
|
for e in m.find(&self.filter) {
|
||||||
|
if m.get_component(e, self.movement).is_some() {
|
||||||
|
// Player's handle their own phyiscs
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let vel = m.get_component_mut(e, self.velocity).unwrap();
|
||||||
|
|
||||||
|
vel.velocity.y -= 0.03;
|
||||||
|
if vel.velocity.y < -0.3 {
|
||||||
|
vel.velocity.y = -0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateLastPosition {
|
||||||
|
filter: ecs::Filter,
|
||||||
|
position: ecs::Key<Position>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateLastPosition {
|
||||||
|
pub fn new(m: &mut ecs::Manager) -> UpdateLastPosition {
|
||||||
|
let position = m.get_key();
|
||||||
|
UpdateLastPosition {
|
||||||
|
filter: ecs::Filter::new()
|
||||||
|
.with(position),
|
||||||
|
position: position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ecs::System for UpdateLastPosition {
|
||||||
|
|
||||||
|
fn update(&mut self, m: &mut ecs::Manager) {
|
||||||
|
for e in m.find(&self.filter) {
|
||||||
|
let pos = m.get_component_mut(e, self.position).unwrap();
|
||||||
|
|
||||||
|
pos.last_position = pos.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/main.rs
21
src/main.rs
|
@ -64,6 +64,7 @@ pub mod world;
|
||||||
pub mod chunk_builder;
|
pub mod chunk_builder;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
pub mod entity;
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock, Mutex};
|
use std::sync::{Arc, RwLock, Mutex};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -108,9 +109,10 @@ impl Game {
|
||||||
|
|
||||||
pub fn tick(&mut self, delta: f64) {
|
pub fn tick(&mut self, delta: f64) {
|
||||||
if !self.server.is_connected() {
|
if !self.server.is_connected() {
|
||||||
self.server.yaw += 0.005 * delta;
|
let rotation = self.server.entities.get_component_mut(self.server.player, self.server.rotation).unwrap();
|
||||||
if self.server.yaw > ::std::f64::consts::PI * 2.0 {
|
rotation.yaw += 0.005 * delta;
|
||||||
self.server.yaw = 0.0;
|
if rotation.yaw > ::std::f64::consts::PI * 2.0 {
|
||||||
|
rotation.yaw = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut clear_reply = false;
|
let mut clear_reply = false;
|
||||||
|
@ -270,13 +272,14 @@ fn handle_window_event(window: &sdl2::video::Window,
|
||||||
}
|
}
|
||||||
let s = 2000.0 + 0.01;
|
let s = 2000.0 + 0.01;
|
||||||
let (rx, ry) = (xrel as f64 / s, yrel as f64 / s);
|
let (rx, ry) = (xrel as f64 / s, yrel as f64 / s);
|
||||||
game.server.yaw -= rx;
|
let rotation = game.server.entities.get_component_mut(game.server.player, game.server.rotation).unwrap();
|
||||||
game.server.pitch -= ry;
|
rotation.yaw -= rx;
|
||||||
if game.server.pitch < (PI/2.0) + 0.01 {
|
rotation.pitch -= ry;
|
||||||
game.server.pitch = (PI/2.0) + 0.01;
|
if rotation.pitch < (PI/2.0) + 0.01 {
|
||||||
|
rotation.pitch = (PI/2.0) + 0.01;
|
||||||
}
|
}
|
||||||
if game.server.pitch > (PI/2.0)*3.0 - 0.01 {
|
if rotation.pitch > (PI/2.0)*3.0 - 0.01 {
|
||||||
game.server.pitch = (PI/2.0)*3.0 - 0.01;
|
rotation.pitch = (PI/2.0)*3.0 - 0.01;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ui_container.hover_at(game, x as f64, y as f64, width as f64, height as f64);
|
ui_container.hover_at(game, x as f64, y as f64, width as f64, height as f64);
|
||||||
|
|
404
src/server.rs
404
src/server.rs
|
@ -25,15 +25,19 @@ use openssl;
|
||||||
use console;
|
use console;
|
||||||
use render;
|
use render;
|
||||||
use auth;
|
use auth;
|
||||||
use cgmath::{self, Vector, Point3};
|
use ecs;
|
||||||
use collision::{Aabb, Aabb3};
|
use entity;
|
||||||
|
use cgmath;
|
||||||
|
use collision::Aabb;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
|
use types::Gamemode;
|
||||||
|
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
conn: Option<protocol::Conn>,
|
conn: Option<protocol::Conn>,
|
||||||
read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>,
|
read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>,
|
||||||
|
|
||||||
pub world: world::World,
|
pub world: world::World,
|
||||||
|
pub entities: ecs::Manager,
|
||||||
world_age: i64,
|
world_age: i64,
|
||||||
world_time: f64,
|
world_time: f64,
|
||||||
world_time_target: f64,
|
world_time_target: f64,
|
||||||
|
@ -43,62 +47,22 @@ pub struct Server {
|
||||||
console: Arc<Mutex<console::Console>>,
|
console: Arc<Mutex<console::Console>>,
|
||||||
version: usize,
|
version: usize,
|
||||||
|
|
||||||
pub position: cgmath::Vector3<f64>,
|
// Entity accessors
|
||||||
last_position: cgmath::Vector3<f64>,
|
world_proxy: ecs::Key<entity::Proxy<world::World>>,
|
||||||
pub yaw: f64,
|
game_info: ecs::Key<entity::GameInfo>,
|
||||||
pub pitch: f64,
|
player_movement: ecs::Key<entity::player::PlayerMovement>,
|
||||||
bounds: Aabb3<f64>,
|
gravity: ecs::Key<entity::Gravity>,
|
||||||
gamemode: Gamemode,
|
position: ecs::Key<entity::Position>,
|
||||||
flying: bool,
|
gamemode: ecs::Key<Gamemode>,
|
||||||
on_ground: bool,
|
pub rotation: ecs::Key<entity::Rotation>,
|
||||||
did_touch_ground: bool,
|
//
|
||||||
v_speed: f64,
|
|
||||||
|
pub player: ecs::Entity,
|
||||||
|
|
||||||
pressed_keys: HashMap<Keycode, bool>,
|
pressed_keys: HashMap<Keycode, bool>,
|
||||||
|
|
||||||
tick_timer: f64,
|
tick_timer: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum Gamemode {
|
|
||||||
Survival = 0,
|
|
||||||
Creative = 1,
|
|
||||||
Adventure = 2,
|
|
||||||
Spectator = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Gamemode {
|
|
||||||
pub fn from_int(val: i32) -> Gamemode {
|
|
||||||
match val {
|
|
||||||
3 => Gamemode::Spectator,
|
|
||||||
2 => Gamemode::Adventure,
|
|
||||||
1 => Gamemode::Creative,
|
|
||||||
0 | _ => Gamemode::Survival,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn can_fly(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Gamemode::Creative | Gamemode::Spectator => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn always_fly(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Gamemode::Spectator => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn noclip(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Gamemode::Spectator => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! handle_packet {
|
macro_rules! handle_packet {
|
||||||
($s:ident $pck:ident {
|
($s:ident $pck:ident {
|
||||||
$($packet:ident => $func:ident,)*
|
$($packet:ident => $func:ident,)*
|
||||||
|
@ -209,8 +173,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server.gamemode = Gamemode::Spectator;
|
*server.entities.get_component_mut(server.player, server.gamemode).unwrap() = Gamemode::Spectator;
|
||||||
server.flying = false;
|
|
||||||
server
|
server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,6 +181,16 @@ impl Server {
|
||||||
resources: Arc<RwLock<resources::Manager>>, console: Arc<Mutex<console::Console>>,
|
resources: Arc<RwLock<resources::Manager>>, console: Arc<Mutex<console::Console>>,
|
||||||
conn: Option<protocol::Conn>, read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>
|
conn: Option<protocol::Conn>, read_queue: Option<mpsc::Receiver<Result<packet::Packet, protocol::Error>>>
|
||||||
) -> Server {
|
) -> Server {
|
||||||
|
let mut entities = ecs::Manager::new();
|
||||||
|
entity::add_systems(&mut entities);
|
||||||
|
|
||||||
|
let player = entity::player::create_local(&mut entities);
|
||||||
|
let world_entity = entities.get_world();
|
||||||
|
let world_proxy = entities.get_key();
|
||||||
|
entities.add_component(world_entity, world_proxy, entity::Proxy::new());
|
||||||
|
let game_info = entities.get_key();
|
||||||
|
entities.add_component(world_entity, game_info, entity::GameInfo::new());
|
||||||
|
|
||||||
let version = resources.read().unwrap().version();
|
let version = resources.read().unwrap().version();
|
||||||
Server {
|
Server {
|
||||||
conn: conn,
|
conn: conn,
|
||||||
|
@ -235,19 +208,18 @@ impl Server {
|
||||||
|
|
||||||
pressed_keys: HashMap::new(),
|
pressed_keys: HashMap::new(),
|
||||||
|
|
||||||
position: cgmath::Vector3::new(0.5, 13.2, 0.5),
|
// Entity accessors
|
||||||
last_position: cgmath::Vector3::zero(),
|
world_proxy: world_proxy,
|
||||||
yaw: 0.0,
|
game_info: game_info,
|
||||||
pitch: 0.0,
|
player_movement: entities.get_key(),
|
||||||
bounds: Aabb3::new(
|
gravity: entities.get_key(),
|
||||||
Point3::new(-0.3, 0.0, -0.3),
|
position: entities.get_key(),
|
||||||
Point3::new(0.3, 1.8, 0.3)
|
gamemode: entities.get_key(),
|
||||||
),
|
rotation: entities.get_key(),
|
||||||
gamemode: Gamemode::Survival,
|
//
|
||||||
flying: false,
|
entities: entities,
|
||||||
on_ground: false,
|
|
||||||
did_touch_ground: false,
|
player: player,
|
||||||
v_speed: 0.0,
|
|
||||||
|
|
||||||
tick_timer: 0.0,
|
tick_timer: 0.0,
|
||||||
}
|
}
|
||||||
|
@ -285,114 +257,37 @@ impl Server {
|
||||||
self.read_queue = Some(rx);
|
self.read_queue = Some(rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flying |= self.gamemode.always_fly();
|
|
||||||
self.last_position = self.position;
|
|
||||||
if self.world.is_chunk_loaded((self.position.x as i32) >> 4, (self.position.z as i32) >> 4) {
|
|
||||||
let (forward, yaw) = self.calculate_movement();
|
|
||||||
let mut speed = 4.317 / 60.0;
|
|
||||||
if self.is_key_pressed(Keycode::LShift) {
|
|
||||||
speed = 5.612 / 60.0;
|
|
||||||
}
|
|
||||||
if self.flying {
|
|
||||||
speed *= 2.5;
|
|
||||||
|
|
||||||
if self.is_key_pressed(Keycode::Space) {
|
|
||||||
self.position.y += speed * delta;
|
|
||||||
}
|
|
||||||
if self.is_key_pressed(Keycode::LCtrl) {
|
|
||||||
self.position.y -= speed * delta;
|
|
||||||
}
|
|
||||||
} else if self.on_ground {
|
|
||||||
if self.is_key_pressed(Keycode::Space) {
|
|
||||||
self.v_speed = 0.15;
|
|
||||||
} else {
|
|
||||||
self.v_speed = 0.0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.v_speed -= 0.01 * delta;
|
|
||||||
if self.v_speed < -0.3 {
|
|
||||||
self.v_speed = -0.3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.position.x += forward * yaw.cos() * delta * speed;
|
|
||||||
self.position.z -= forward * yaw.sin() * delta * speed;
|
|
||||||
self.position.y += self.v_speed * delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.gamemode.noclip() {
|
|
||||||
let mut target = self.position;
|
|
||||||
self.position.y = self.last_position.y;
|
|
||||||
self.position.z = self.last_position.z;
|
|
||||||
|
|
||||||
// We handle each axis separately to allow for a sliding
|
|
||||||
// effect when pushing up against walls.
|
|
||||||
|
|
||||||
let (bounds, xhit) = self.check_collisions(self.bounds);
|
|
||||||
self.position.x = bounds.min.x + 0.3;
|
|
||||||
self.last_position.x = self.position.x;
|
|
||||||
|
|
||||||
self.position.z = target.z;
|
|
||||||
let (bounds, zhit) = self.check_collisions(self.bounds);
|
|
||||||
self.position.z = bounds.min.z + 0.3;
|
|
||||||
self.last_position.z = self.position.z;
|
|
||||||
|
|
||||||
// Half block jumps
|
|
||||||
// Minecraft lets you 'jump' up 0.5 blocks
|
|
||||||
// for slabs and stairs (or smaller blocks).
|
|
||||||
// Currently we implement this as a teleport to the
|
|
||||||
// top of the block if we could move there
|
|
||||||
// but this isn't smooth.
|
|
||||||
if (xhit || zhit) && self.on_ground {
|
|
||||||
let mut ox = self.position.x;
|
|
||||||
let mut oz = self.position.z;
|
|
||||||
self.position.x = target.x;
|
|
||||||
self.position.z = target.z;
|
|
||||||
for offset in 1 .. 9 {
|
|
||||||
let mini = self.bounds.add_v(cgmath::Vector3::new(0.0, offset as f64 / 16.0, 0.0));
|
|
||||||
let (_, hit) = self.check_collisions(mini);
|
|
||||||
if !hit {
|
|
||||||
target.y += offset as f64 / 16.0;
|
|
||||||
ox = target.x;
|
|
||||||
oz = target.z;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.position.x = ox;
|
|
||||||
self.position.z = oz;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.position.y = target.y;
|
|
||||||
let (bounds, yhit) = self.check_collisions(self.bounds);
|
|
||||||
self.position.y = bounds.min.y;
|
|
||||||
self.last_position.y = self.position.y;
|
|
||||||
if yhit {
|
|
||||||
self.v_speed = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ground = Aabb3::new(
|
|
||||||
Point3::new(-0.3, -0.05, -0.3),
|
|
||||||
Point3::new(0.3, 0.0, 0.3)
|
|
||||||
);
|
|
||||||
let prev = self.on_ground;
|
|
||||||
let (_, hit) = self.check_collisions(ground);
|
|
||||||
self.on_ground = hit;
|
|
||||||
if !prev && self.on_ground {
|
|
||||||
self.did_touch_ground = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tick_timer += delta;
|
self.tick_timer += delta;
|
||||||
while self.tick_timer >= 3.0 && self.is_connected() {
|
while self.tick_timer >= 3.0 && self.is_connected() {
|
||||||
self.minecraft_tick();
|
self.minecraft_tick();
|
||||||
self.tick_timer -= 3.0;
|
self.tick_timer -= 3.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.render_tick(delta);
|
||||||
|
|
||||||
self.update_time(renderer, delta);
|
self.update_time(renderer, delta);
|
||||||
|
|
||||||
// Copy to camera
|
// Copy to camera
|
||||||
renderer.camera.pos = cgmath::Point::from_vec(self.position + cgmath::Vector3::new(0.0, 1.62, 0.0));
|
let position = self.entities.get_component(self.player, self.position).unwrap();
|
||||||
renderer.camera.yaw = self.yaw;
|
let rotation = self.entities.get_component(self.player, self.rotation).unwrap();
|
||||||
renderer.camera.pitch = self.pitch;
|
renderer.camera.pos = cgmath::Point::from_vec(position.position + cgmath::Vector3::new(0.0, 1.62, 0.0));
|
||||||
|
renderer.camera.yaw = rotation.yaw;
|
||||||
|
renderer.camera.pitch = rotation.pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_tick(&mut self, delta: f64) {
|
||||||
|
let world_entity = self.entities.get_world();
|
||||||
|
self.entities.get_component_mut(world_entity, self.world_proxy)
|
||||||
|
.unwrap()
|
||||||
|
.give(&mut self.world);
|
||||||
|
self.entities.get_component_mut(world_entity, self.game_info)
|
||||||
|
.unwrap().delta = delta;
|
||||||
|
|
||||||
|
self.entities.render_tick();
|
||||||
|
|
||||||
|
self.entities.get_component_mut(world_entity, self.world_proxy)
|
||||||
|
.unwrap()
|
||||||
|
.take(&mut self.world);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_time(&mut self, renderer: &mut render::Renderer, delta: f64) {
|
fn update_time(&mut self, renderer: &mut render::Renderer, delta: f64) {
|
||||||
|
@ -436,85 +331,39 @@ impl Server {
|
||||||
offset * 0.8 + 0.2
|
offset * 0.8 + 0.2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_collisions(&self, bounds: Aabb3<f64>) -> (Aabb3<f64>, bool) {
|
|
||||||
let mut bounds = bounds.add_v(self.position);
|
|
||||||
|
|
||||||
let dir = self.position - self.last_position;
|
|
||||||
|
|
||||||
let min_x = (bounds.min.x - 1.0) as i32;
|
|
||||||
let min_y = (bounds.min.y - 1.0) as i32;
|
|
||||||
let min_z = (bounds.min.z - 1.0) as i32;
|
|
||||||
let max_x = (bounds.max.x + 1.0) as i32;
|
|
||||||
let max_y = (bounds.max.y + 1.0) as i32;
|
|
||||||
let max_z = (bounds.max.z + 1.0) as i32;
|
|
||||||
|
|
||||||
let mut hit = false;
|
|
||||||
for y in min_y .. max_y {
|
|
||||||
for z in min_z .. max_z {
|
|
||||||
for x in min_x .. max_x {
|
|
||||||
let block = self.world.get_block(x, y, z);
|
|
||||||
for bb in block.get_collision_boxes() {
|
|
||||||
let bb = bb.add_v(cgmath::Vector3::new(x as f64, y as f64, z as f64));
|
|
||||||
if bb.collides(&bounds) {
|
|
||||||
bounds = bounds.move_out_of(bb, dir);
|
|
||||||
hit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(bounds, hit)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_movement(&self) -> (f64, f64) {
|
|
||||||
use std::f64::consts::PI;
|
|
||||||
let mut forward = 0.0f64;
|
|
||||||
let mut yaw = self.yaw - (PI/2.0);
|
|
||||||
if self.is_key_pressed(Keycode::W) || self.is_key_pressed(Keycode::S) {
|
|
||||||
forward = 1.0;
|
|
||||||
if self.is_key_pressed(Keycode::S) {
|
|
||||||
yaw += PI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut change = 0.0;
|
|
||||||
if self.is_key_pressed(Keycode::A) {
|
|
||||||
change = (PI / 2.0) / (forward.abs() + 1.0);
|
|
||||||
}
|
|
||||||
if self.is_key_pressed(Keycode::D) {
|
|
||||||
change = -(PI / 2.0) / (forward.abs() + 1.0);
|
|
||||||
}
|
|
||||||
if self.is_key_pressed(Keycode::A) || self.is_key_pressed(Keycode::D) {
|
|
||||||
forward = 1.0;
|
|
||||||
}
|
|
||||||
if self.is_key_pressed(Keycode::S) {
|
|
||||||
yaw -= change;
|
|
||||||
} else {
|
|
||||||
yaw += change;
|
|
||||||
}
|
|
||||||
|
|
||||||
(forward, yaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn minecraft_tick(&mut self) {
|
pub fn minecraft_tick(&mut self) {
|
||||||
|
let world_entity = self.entities.get_world();
|
||||||
|
self.entities.get_component_mut(world_entity, self.world_proxy)
|
||||||
|
.unwrap()
|
||||||
|
.give(&mut self.world);
|
||||||
|
self.entities.tick();
|
||||||
|
self.entities.get_component_mut(world_entity, self.world_proxy)
|
||||||
|
.unwrap()
|
||||||
|
.take(&mut self.world);
|
||||||
|
|
||||||
|
let movement = self.entities.get_component_mut(self.player, self.player_movement).unwrap();
|
||||||
|
let on_ground = self.entities.get_component(self.player, self.gravity).map_or(false, |v| v.on_ground);
|
||||||
|
let position = self.entities.get_component(self.player, self.position).unwrap();
|
||||||
|
let rotation = self.entities.get_component(self.player, self.rotation).unwrap();
|
||||||
|
|
||||||
// Force the server to know when touched the ground
|
// Force the server to know when touched the ground
|
||||||
// otherwise if it happens between ticks the server
|
// otherwise if it happens between ticks the server
|
||||||
// will think we are flying.
|
// will think we are flying.
|
||||||
let on_ground = if self.did_touch_ground {
|
let on_ground = if movement.did_touch_ground {
|
||||||
self.did_touch_ground = false;
|
movement.did_touch_ground = false;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
self.on_ground
|
on_ground
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sync our position to the server
|
// Sync our position to the server
|
||||||
// Use the smaller packets when possible
|
// Use the smaller packets when possible
|
||||||
let packet = packet::play::serverbound::PlayerPositionLook {
|
let packet = packet::play::serverbound::PlayerPositionLook {
|
||||||
x: self.position.x,
|
x: position.position.x,
|
||||||
y: self.position.y,
|
y: position.position.y,
|
||||||
z: self.position.z,
|
z: position.position.z,
|
||||||
yaw: self.yaw as f32,
|
yaw: rotation.yaw as f32,
|
||||||
pitch: self.pitch as f32,
|
pitch: rotation.pitch as f32,
|
||||||
on_ground: on_ground,
|
on_ground: on_ground,
|
||||||
};
|
};
|
||||||
self.write_packet(packet);
|
self.write_packet(packet);
|
||||||
|
@ -522,6 +371,9 @@ impl Server {
|
||||||
|
|
||||||
pub fn key_press(&mut self, down: bool, key: Keycode) {
|
pub fn key_press(&mut self, down: bool, key: Keycode) {
|
||||||
self.pressed_keys.insert(key, down);
|
self.pressed_keys.insert(key, down);
|
||||||
|
if let Some(movement) = self.entities.get_component_mut(self.player, self.player_movement) {
|
||||||
|
movement.pressed_keys.insert(key, down);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_key_pressed(&self, key: Keycode) -> bool {
|
fn is_key_pressed(&self, key: Keycode) -> bool {
|
||||||
|
@ -539,16 +391,18 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_game_join(&mut self, join: packet::play::clientbound::JoinGame) {
|
fn on_game_join(&mut self, join: packet::play::clientbound::JoinGame) {
|
||||||
self.gamemode = Gamemode::from_int((join.gamemode & 0x7) as i32);
|
let gamemode = Gamemode::from_int((join.gamemode & 0x7) as i32);
|
||||||
|
*self.entities.get_component_mut(self.player, self.gamemode).unwrap() = gamemode;
|
||||||
// TODO: Temp
|
// TODO: Temp
|
||||||
self.flying = self.gamemode.can_fly();
|
self.entities.get_component_mut(self.player, self.player_movement).unwrap().flying = gamemode.can_fly();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_respawn(&mut self, respawn: packet::play::clientbound::Respawn) {
|
fn on_respawn(&mut self, respawn: packet::play::clientbound::Respawn) {
|
||||||
self.world = world::World::new();
|
self.world = world::World::new();
|
||||||
self.gamemode = Gamemode::from_int((respawn.gamemode & 0x7) as i32);
|
let gamemode = Gamemode::from_int((respawn.gamemode & 0x7) as i32);
|
||||||
|
*self.entities.get_component_mut(self.player, self.gamemode).unwrap() = gamemode;
|
||||||
// TODO: Temp
|
// TODO: Temp
|
||||||
self.flying = self.gamemode.can_fly();
|
self.entities.get_component_mut(self.player, self.player_movement).unwrap().flying = gamemode.can_fly();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_time_update(&mut self, time_update: packet::play::clientbound::TimeUpdate) {
|
fn on_time_update(&mut self, time_update: packet::play::clientbound::TimeUpdate) {
|
||||||
|
@ -565,21 +419,24 @@ impl Server {
|
||||||
fn on_game_state_change(&mut self, game_state: packet::play::clientbound::ChangeGameState) {
|
fn on_game_state_change(&mut self, game_state: packet::play::clientbound::ChangeGameState) {
|
||||||
match game_state.reason {
|
match game_state.reason {
|
||||||
3 => {
|
3 => {
|
||||||
self.gamemode = Gamemode::from_int(game_state.value as i32);
|
let gamemode = Gamemode::from_int(game_state.value as i32);
|
||||||
|
*self.entities.get_component_mut(self.player, self.gamemode).unwrap() = gamemode;
|
||||||
// TODO: Temp
|
// TODO: Temp
|
||||||
self.flying = self.gamemode.can_fly();
|
self.entities.get_component_mut(self.player, self.player_movement).unwrap().flying = gamemode.can_fly();
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_teleport(&mut self, teleport: packet::play::clientbound::TeleportPlayer) {
|
fn on_teleport(&mut self, teleport: packet::play::clientbound::TeleportPlayer) {
|
||||||
|
let position = self.entities.get_component_mut(self.player, self.position).unwrap();
|
||||||
|
let rotation = self.entities.get_component_mut(self.player, self.rotation).unwrap();
|
||||||
// TODO: relative teleports
|
// TODO: relative teleports
|
||||||
self.position.x = teleport.x;
|
position.position.x = teleport.x;
|
||||||
self.position.y = teleport.y;
|
position.position.y = teleport.y;
|
||||||
self.position.z = teleport.z;
|
position.position.z = teleport.z;
|
||||||
self.yaw = teleport.yaw as f64;
|
rotation.yaw = teleport.yaw as f64;
|
||||||
self.pitch = teleport.pitch as f64;
|
rotation.pitch = teleport.pitch as f64;
|
||||||
|
|
||||||
self.write_packet(packet::play::serverbound::TeleportConfirm {
|
self.write_packet(packet::play::serverbound::TeleportConfirm {
|
||||||
teleport_id: teleport.teleport_id,
|
teleport_id: teleport.teleport_id,
|
||||||
|
@ -600,58 +457,3 @@ impl Server {
|
||||||
self.world.unload_chunk(chunk_unload.x, chunk_unload.z);
|
self.world.unload_chunk(chunk_unload.x, chunk_unload.z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Collidable<T> {
|
|
||||||
fn collides(&self, t: &T) -> bool;
|
|
||||||
fn move_out_of(self, other: Self, dir: cgmath::Vector3<f64>) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Collidable<Aabb3<f64>> for Aabb3<f64> {
|
|
||||||
fn collides(&self, t: &Aabb3<f64>) -> bool {
|
|
||||||
!(
|
|
||||||
t.min.x >= self.max.x ||
|
|
||||||
t.max.x <= self.min.x ||
|
|
||||||
t.min.y >= self.max.y ||
|
|
||||||
t.max.y <= self.min.y ||
|
|
||||||
t.min.z >= self.max.z ||
|
|
||||||
t.max.z <= self.min.z
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn move_out_of(mut self, other: Self, dir: cgmath::Vector3<f64>) -> Self {
|
|
||||||
if dir.x != 0.0 {
|
|
||||||
if dir.x > 0.0 {
|
|
||||||
let ox = self.max.x;
|
|
||||||
self.max.x = other.min.x - 0.0001;
|
|
||||||
self.min.x += self.max.x - ox;
|
|
||||||
} else {
|
|
||||||
let ox = self.min.x;
|
|
||||||
self.min.x = other.max.x + 0.0001;
|
|
||||||
self.max.x += self.min.x - ox;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dir.y != 0.0 {
|
|
||||||
if dir.y > 0.0 {
|
|
||||||
let oy = self.max.y;
|
|
||||||
self.max.y = other.min.y - 0.0001;
|
|
||||||
self.min.y += self.max.y - oy;
|
|
||||||
} else {
|
|
||||||
let oy = self.min.y;
|
|
||||||
self.min.y = other.max.y + 0.0001;
|
|
||||||
self.max.y += self.min.y - oy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dir.z != 0.0 {
|
|
||||||
if dir.z > 0.0 {
|
|
||||||
let oz = self.max.z;
|
|
||||||
self.max.z = other.min.z - 0.0001;
|
|
||||||
self.min.z += self.max.z - oz;
|
|
||||||
} else {
|
|
||||||
let oz = self.min.z;
|
|
||||||
self.min.z = other.max.z + 0.0001;
|
|
||||||
self.max.z += self.min.z - oz;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -140,3 +140,45 @@ impl Direction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Gamemode {
|
||||||
|
Survival = 0,
|
||||||
|
Creative = 1,
|
||||||
|
Adventure = 2,
|
||||||
|
Spectator = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gamemode {
|
||||||
|
pub fn from_int(val: i32) -> Gamemode {
|
||||||
|
match val {
|
||||||
|
3 => Gamemode::Spectator,
|
||||||
|
2 => Gamemode::Adventure,
|
||||||
|
1 => Gamemode::Creative,
|
||||||
|
0 | _ => Gamemode::Survival,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_fly(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Gamemode::Creative | Gamemode::Spectator => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn always_fly(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Gamemode::Spectator => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn noclip(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Gamemode::Spectator => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue