Block entity support, implement signs

This commit is contained in:
Thinkofname 2016-04-04 22:08:24 +01:00
parent 3fb58a1b2c
commit c117f28b2a
10 changed files with 434 additions and 22 deletions

View File

@ -1303,7 +1303,7 @@ define_blocks! {
],
},
data Some(rotation.data()),
material material::NON_SOLID,
material material::INVISIBLE,
model { ("minecraft", "standing_sign") },
collision vec![],
}
@ -1412,7 +1412,7 @@ define_blocks! {
_ => 2,
})
},
material material::NON_SOLID,
material material::INVISIBLE,
model { ("minecraft", "wall_sign") },
variant format!("facing={}", facing.as_string()),
collision vec![],
@ -5344,7 +5344,7 @@ impl Rotation {
}
}
fn data(&self) -> usize {
pub fn data(&self) -> usize {
match *self {
Rotation::South => 0,
Rotation::SouthSouthWest => 1,

View File

@ -3,7 +3,7 @@ use std::fmt;
use direction::Direction;
use std::ops;
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
pub x: i32,
pub y: i32,

View File

@ -170,7 +170,13 @@ impl Manager {
let changes = self.changed_entity_components.clone();
self.changed_entity_components = HashSet::with_hasher(BuildHasherDefault::default());
for entity in changes {
let state = self.entities[entity.id].0.as_mut().unwrap().clone();
let (cur, state) = {
let state = self.entities[entity.id].0.as_mut().unwrap();
let cur = state.components.clone();
let orig = state.clone();
state.components.or(&state.last_components);
(cur, orig)
};
self.trigger_add_for_systems(entity, &state.last_components, &state.components, world, renderer);
self.trigger_add_for_render_systems(entity, &state.last_components, &state.components, world, renderer);
self.trigger_remove_for_systems(entity, &state.last_components, &state.components, world, renderer);
@ -182,12 +188,14 @@ impl Manager {
}
}
{
let state = self.entities[entity.id].0.as_mut().unwrap();
state.components = cur;
state.last_components = state.components.clone();
}
if state.removed {
self.free_entities.push(entity.id);
self.entities[entity.id].0 = None;
} else {
let state = self.entities[entity.id].0.as_mut().unwrap();
state.last_components = state.components.clone();
}
}
}
@ -244,6 +252,7 @@ impl Manager {
if let Some(set) = self.entities[e.id].0.as_mut() {
set.components = BSet::new(self.components.len());
set.removed = true;
self.changed_entity_components.insert(e);
}
}

View File

@ -0,0 +1,32 @@
pub mod sign;
use world::block::Block;
use shared::Position;
use ecs;
pub fn add_systems(m: &mut ecs::Manager) {
sign::add_systems(m);
}
pub enum BlockEntityType {
Sign
}
impl BlockEntityType {
pub fn get_block_entity(bl: Block) -> Option<BlockEntityType> {
match bl {
Block::StandingSign{..} | Block::WallSign{..} => Some(BlockEntityType::Sign),
_ => None,
}
}
pub fn create_entity(&self, m: &mut ecs::Manager, pos: Position) -> ecs::Entity {
let e = m.create_entity();
m.add_component_direct(e, pos);
match *self {
BlockEntityType::Sign => sign::init_entity(m, e),
}
e
}
}

View File

@ -0,0 +1,263 @@
use ecs;
use format::{self, Component};
use shared::{Direction, Position};
use world;
use world::block::Block;
use render;
use render::model;
pub fn add_systems(m: &mut ecs::Manager) {
let sys = SignRenderer::new(m);
m.add_render_system(sys);
}
pub fn init_entity(m: &mut ecs::Manager, e: ecs::Entity) {
m.add_component_direct(e, SignInfo {
model: None,
lines: [
Component::Text(format::TextComponent::new("")),
Component::Text(format::TextComponent::new("")),
Component::Text(format::TextComponent::new("")),
Component::Text(format::TextComponent::new("")),
],
offset_x: 0.0,
offset_y: 0.0,
offset_z: 0.0,
has_stand: false,
rotation: 0.0,
dirty: false,
});
}
pub struct SignInfo {
model: Option<model::ModelKey>,
pub lines: [format::Component; 4],
pub dirty: bool,
offset_x: f64,
offset_y: f64,
offset_z: f64,
has_stand: bool,
rotation: f64,
}
struct SignRenderer {
filter: ecs::Filter,
position: ecs::Key<Position>,
sign_info: ecs::Key<SignInfo>,
}
impl SignRenderer {
fn new(m: &mut ecs::Manager) -> SignRenderer {
let sign_info = m.get_key();
let position = m.get_key();
SignRenderer {
filter: ecs::Filter::new()
.with(position)
.with(sign_info),
position: position,
sign_info: sign_info,
}
}
}
impl ecs::System for SignRenderer {
fn filter(&self) -> &ecs::Filter {
&self.filter
}
fn update(&mut self, m: &mut ecs::Manager, world: &mut world::World, renderer: &mut render::Renderer) {
for e in m.find(&self.filter) {
let position = *m.get_component(e, self.position).unwrap();
let info = m.get_component_mut(e, self.sign_info).unwrap();
if info.dirty {
self.entity_removed(m, e, world, renderer);
self.entity_added(m, e, world, renderer);
}
if let Some(model) = info.model {
let mdl = renderer.model.get_model(model).unwrap();
mdl.block_light = world.get_block_light(position) as f32;
mdl.sky_light = world.get_sky_light(position) as f32;
}
}
}
fn entity_added(&mut self, m: &mut ecs::Manager, e: ecs::Entity, world: &mut world::World, renderer: &mut render::Renderer) {
use std::f64::consts::PI;
use cgmath::{Vector3, Matrix4, Decomposed, Rotation3, Rad, Angle, Quaternion};
let position = *m.get_component(e, self.position).unwrap();
let info = m.get_component_mut(e, self.sign_info).unwrap();
info.dirty = false;
match world.get_block(position) {
Block::WallSign{facing} => {
info.offset_z = 7.5 / 16.0;
match facing {
Direction::North => {},
Direction::South => info.rotation = PI,
Direction::West => info.rotation = PI / 2.0,
Direction::East => info.rotation = -PI / 2.0,
_ => unreachable!(),
}
},
Block::StandingSign{rotation} => {
info.offset_z = 5.0 / 16.0;
info.has_stand = true;
info.rotation = -(rotation.data() as f64 / 16.0) * PI * 2.0 + PI;
}
_ => return,
}
let wood = render::Renderer::get_texture(renderer.get_textures_ref(), "blocks/planks_oak");
macro_rules! rel {
($tex:expr, $x:expr, $y:expr, $w:expr, $h:expr) => (
Some($tex.relative(($x) / 16.0, ($y) / 16.0, ($w) / 16.0, ($h) / 16.0))
);
}
let mut verts = vec![];
// Backboard
model::append_box_texture_scale(&mut verts, -0.5, -4.0/16.0, -0.5/16.0, 1.0, 8.0/16.0, 1.0/16.0, [
rel!(wood, 0.0, 0.0, 16.0, 2.0), // Up
rel!(wood, 0.0, 0.0, 16.0, 2.0), // Down
rel!(wood, 0.0, 4.0, 16.0, 12.0), // North
rel!(wood, 0.0, 4.0, 16.0, 12.0), // South
rel!(wood, 0.0, 0.0, 2.0, 12.0), // West
rel!(wood, 0.0, 0.0, 2.0, 12.0), // East
], [
[1.5, 1.0], // Up
[1.5, 1.0], // Down
[1.5, 1.0], // North
[1.5, 1.0], // South
[1.0, 1.0], // West
[1.0, 1.0], // East
]);
for vert in &mut verts[8..12] {
vert.r = 183;
vert.g = 183;
vert.b = 196;
}
if info.has_stand {
let log = render::Renderer::get_texture(renderer.get_textures_ref(), "blocks/log_oak");
model::append_box(&mut verts, -0.5/16.0, -0.25-9.0/16.0, -0.5/16.0, 1.0/16.0, 9.0/16.0, 1.0/16.0, [
rel!(log, 0.0, 0.0, 2.0, 2.0), // Up
rel!(log, 0.0, 0.0, 2.0, 2.0), // Down
rel!(log, 0.0, 4.0, 2.0, 12.0), // North
rel!(log, 0.0, 4.0, 2.0, 12.0), // South
rel!(log, 0.0, 0.0, 2.0, 12.0), // West
rel!(log, 0.0, 0.0, 2.0, 12.0), // East
]);
}
for (i, line) in info.lines.iter().enumerate() {
let mut state = FormatState {
line: i as f32,
width: 0.0,
offset: 0.0,
text: Vec::new(),
renderer: renderer,
};
state.build(line, format::Color::Black);
let width = state.width;
// Center align text
for vert in &mut state.text {
vert.x += width * 0.5;
}
verts.extend_from_slice(&state.text);
}
let model = renderer.model.create_model(
model::DEFAULT,
vec![verts]
);
{
let mdl = renderer.model.get_model(model).unwrap();
mdl.radius = 2.0;
mdl.x = position.x as f32 + 0.5;
mdl.y = position.y as f32 + 0.5;
mdl.z = position.z as f32 + 0.5;
mdl.matrix[0] = Matrix4::from(Decomposed {
scale: 1.0,
rot: Quaternion::from_angle_y(Rad::new(info.rotation as f32)),
disp: Vector3::new(position.x as f32 + 0.5, -position.y as f32 - 0.5, position.z as f32 + 0.5),
}) * Matrix4::from_translation(Vector3::new(info.offset_x as f32, -info.offset_y as f32, info.offset_z as f32));
}
info.model = Some(model);
}
fn entity_removed(&mut self, m: &mut ecs::Manager, e: ecs::Entity, _: &mut world::World, renderer: &mut render::Renderer) {
let info = m.get_component_mut(e, self.sign_info).unwrap();
if let Some(model) = info.model {
renderer.model.remove_model(model);
}
info.model = None;
}
}
struct FormatState<'a> {
line: f32,
offset: f32,
width: f32,
text: Vec<model::Vertex>,
renderer: &'a mut render::Renderer,
}
impl <'a> FormatState<'a> {
fn build(&mut self, c: &Component, color: format::Color) {
match c {
&format::Component::Text(ref txt) => {
let col = FormatState::get_color(&txt.modifier, color);
self.append_text(&txt.text, col);
let modi = &txt.modifier;
if let Some(ref extra) = modi.extra {
for e in extra {
self.build(e, col);
}
}
}
}
}
fn append_text(&mut self, txt: &str, color: format::Color) {
const Y_SCALE: f32 = (6.0 / 16.0) / 4.0;
const X_SCALE: f32 = Y_SCALE / 16.0;
let (rr, gg, bb) = color.to_rgb();
for ch in txt.chars() {
if ch == ' ' {
self.offset += 6.0 * X_SCALE;
continue;
}
let texture = self.renderer.ui.character_texture(ch);
let w = self.renderer.ui.size_of_char(ch) as f32;
for vert in ::model::BlockVertex::face_by_direction(Direction::North) {
self.text.push(model::Vertex {
x: vert.x * X_SCALE * w - (self.offset + w * X_SCALE),
y: vert.y * Y_SCALE - (Y_SCALE + 0.4/16.0) * self.line + 2.1 / 16.0,
z: -0.6 / 16.0,
texture: texture.clone(),
texture_x: vert.toffsetx as f64,
texture_y: vert.toffsety as f64,
r: rr,
g: gg,
b: bb,
a: 255,
id: 0,
});
}
self.offset += (w + 2.0) * X_SCALE;
}
if self.offset > self.width {
self.width = self.offset;
}
}
fn get_color(modi: &format::Modifier, color: format::Color) -> format::Color {
modi.color.unwrap_or(color)
}
}

View File

@ -1,5 +1,6 @@
pub mod player;
pub mod block_entity;
use ecs;
use cgmath::Vector3;
@ -17,6 +18,8 @@ pub fn add_systems(m: &mut ecs::Manager) {
m.add_system(sys);
let sys = systems::ApplyGravity::new(m);
m.add_system(sys);
block_entity::add_systems(m);
}
/// Location of an entity in the world.

View File

@ -204,7 +204,7 @@ impl Manager {
}
}
pub fn draw(&mut self, frustum: &Frustum<f32>, perspective_matrix: &Matrix4<f32>, camera_matrix: &Matrix4<f32>, sky_offset: f32, light_level: f32) {
pub fn draw(&mut self, frustum: &Frustum<f32>, perspective_matrix: &Matrix4<f32>, camera_matrix: &Matrix4<f32>, light_level: f32, sky_offset: f32) {
let m = if self.index_type == gl::UNSIGNED_SHORT {
2
} else {
@ -359,7 +359,7 @@ pub fn append_box_texture_scale(
z: vert.z * d + z,
texture: tex.clone(),
texture_x: (vert.toffsetx as f64) * texture_scale[dir.index()][0],
texture_y: (vert.toffsety as f64) * texture_scale[dir.index()][0],
texture_y: (vert.toffsety as f64) * texture_scale[dir.index()][1],
r: rr,
g: gg,
b: bb,

View File

@ -306,7 +306,7 @@ impl Server {
sun_model.tick(renderer, self.world_time, self.world_age);
}
self.world.tick();
self.world.tick(&mut self.entities);
// Copy to camera
if let Some(player) = self.player {
@ -340,6 +340,7 @@ impl Server {
TeleportPlayer => on_teleport,
TimeUpdate => on_time_update,
ChangeGameState => on_game_state_change,
UpdateSign => on_sign_update,
}
},
Err(err) => panic!("Err: {:?}", err),
@ -520,6 +521,16 @@ impl Server {
}
}
fn on_sign_update(&mut self, update_sign: packet::play::clientbound::UpdateSign) {
self.world.add_block_entity_action(world::BlockEntityAction::UpdateSignText(
update_sign.location,
update_sign.line1,
update_sign.line2,
update_sign.line3,
update_sign.line4,
));
}
fn on_chunk_data(&mut self, chunk_data: packet::play::clientbound::ChunkData) {
self.world.load_chunk(
chunk_data.chunk_x,
@ -531,7 +542,7 @@ impl Server {
}
fn on_chunk_unload(&mut self, chunk_unload: packet::play::clientbound::ChunkUnload) {
self.world.unload_chunk(chunk_unload.x, chunk_unload.z);
self.world.unload_chunk(chunk_unload.x, chunk_unload.z, &mut self.entities);
}
fn on_block_change(&mut self, block_change: packet::play::clientbound::BlockChange) {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Set {
data: Vec<u64>,
}
@ -65,4 +65,10 @@ impl Set {
}
true
}
pub fn or(&mut self, other: &Set) {
for (a, b) in self.data.iter_mut().zip(&other.data) {
*a = (*a) | *b;
}
}
}

View File

@ -25,6 +25,9 @@ use render;
use collision;
use cgmath;
use chunk_builder;
use ecs;
use entity::block_entity;
use format;
pub mod biome;
@ -34,6 +37,15 @@ pub struct World {
render_list: Vec<(i32, i32, i32)>,
light_updates: VecDeque<LightUpdate>,
block_entity_actions: VecDeque<BlockEntityAction>,
}
#[derive(Clone, Debug)]
pub enum BlockEntityAction {
Create(Position),
Remove(Position),
UpdateSignText(Position, format::Component, format::Component, format::Component, format::Component),
}
#[derive(Clone, Copy, PartialEq, Eq)]
@ -68,6 +80,7 @@ impl World {
chunks: HashMap::with_hasher(BuildHasherDefault::default()),
render_list: vec![],
light_updates: VecDeque::new(),
block_entity_actions: VecDeque::new(),
}
}
@ -84,7 +97,17 @@ impl World {
fn set_block_raw(&mut self, pos: Position, b: block::Block) -> bool {
let cpos = CPos(pos.x >> 4, pos.z >> 4);
let chunk = self.chunks.entry(cpos).or_insert_with(|| Chunk::new(cpos));
chunk.set_block(pos.x & 0xF, pos.y, pos.z & 0xF, b)
if chunk.set_block(pos.x & 0xF, pos.y, pos.z & 0xF, b) {
if chunk.block_entities.contains_key(&pos) {
self.block_entity_actions.push_back(BlockEntityAction::Remove(pos));
}
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
self.block_entity_actions.push_back(BlockEntityAction::Create(pos));
}
true
} else {
false
}
}
pub fn update_block(&mut self, pos: Position) {
@ -133,7 +156,7 @@ impl World {
chunk.set_block_light(pos.x & 0xF, pos.y, pos.z & 0xF, light);
}
fn get_block_light(&self, pos: Position) -> u8 {
pub fn get_block_light(&self, pos: Position) -> u8 {
match self.chunks.get(&CPos(pos.x >> 4, pos.z >> 4)) {
Some(ref chunk) => chunk.get_block_light(pos.x & 0xF, pos.y, pos.z & 0xF),
None => 0,
@ -146,7 +169,7 @@ impl World {
chunk.set_sky_light(pos.x & 0xF, pos.y, pos.z & 0xF, light);
}
fn get_sky_light(&self, pos: Position) -> u8 {
pub fn get_sky_light(&self, pos: Position) -> u8 {
match self.chunks.get(&CPos(pos.x >> 4, pos.z >> 4)) {
Some(ref chunk) => chunk.get_sky_light(pos.x & 0xF, pos.y, pos.z & 0xF),
None => 15,
@ -160,7 +183,11 @@ impl World {
});
}
pub fn tick(&mut self) {
pub fn add_block_entity_action(&mut self, action: BlockEntityAction) {
self.block_entity_actions.push_back(action);
}
pub fn tick(&mut self, m: &mut ecs::Manager) {
use time;
let start = time::precise_time_ns();
let mut updates_performed = 0;
@ -174,6 +201,48 @@ impl World {
}
}
}
let sign_info: ecs::Key<block_entity::sign::SignInfo> = m.get_key();
while let Some(action) = self.block_entity_actions.pop_front() {
match action {
BlockEntityAction::Remove(pos) => {
if let Some(chunk) = self.chunks.get_mut(&CPos(pos.x >> 4, pos.z >> 4)) {
if let Some(entity) = chunk.block_entities.remove(&pos) {
m.remove_entity(entity);
}
}
},
BlockEntityAction::Create(pos) => {
if let Some(chunk) = self.chunks.get_mut(&CPos(pos.x >> 4, pos.z >> 4)) {
// Remove existing entity
if let Some(entity) = chunk.block_entities.remove(&pos) {
m.remove_entity(entity);
}
let block = chunk.get_block(pos.x & 0xF, pos.y, pos.z & 0xF);
if let Some(entity_type) = block_entity::BlockEntityType::get_block_entity(block) {
let entity = entity_type.create_entity(m, pos);
chunk.block_entities.insert(pos, entity);
}
}
},
BlockEntityAction::UpdateSignText(pos, line1, line2, line3, line4) => {
if let Some(chunk) = self.chunks.get(&CPos(pos.x >> 4, pos.z >> 4)) {
if let Some(entity) = chunk.block_entities.get(&pos) {
if let Some(sign) = m.get_component_mut(*entity, sign_info) {
sign.lines = [
line1,
line2,
line3,
line4,
];
sign.dirty = true;
}
}
}
}
}
}
}
fn do_light_update(&mut self) {
@ -471,8 +540,12 @@ impl World {
snapshot
}
pub fn unload_chunk(&mut self, x: i32, z: i32) {
self.chunks.remove(&CPos(x, z));
pub fn unload_chunk(&mut self, x: i32, z: i32, m: &mut ecs::Manager) {
if let Some(chunk) = self.chunks.remove(&CPos(x, z)) {
for entity in chunk.block_entities.values() {
m.remove_entity(*entity);
}
}
}
pub fn load_chunk(&mut self, x: i32, z: i32, new: bool, mask: u16, data: Vec<u8>) -> Result<(), protocol::Error> {
@ -529,8 +602,8 @@ impl World {
let m = bit::Map::from_raw(bits, bit_size as usize);
section.blocks = m;
for i in 0 .. 4096 {
let bl_id = section.blocks.get(i);
for bi in 0 .. 4096 {
let bl_id = section.blocks.get(bi);
if bit_size == 13 {
if section.block_map.get(bl_id)
.map(|v| v.1)
@ -543,7 +616,19 @@ impl World {
section.rev_block_map.insert(bl, bl_id);
}
}
section.block_map.get_mut(bl_id).unwrap().1 += 1;
let bmap = section.block_map.get_mut(bl_id).unwrap();
bmap.1 += 1;
if block_entity::BlockEntityType::get_block_entity(bmap.0).is_some() {
let pos = Position::new(
(bi & 0xF) as i32,
(bi >> 8) as i32,
((bi >> 4) & 0xF) as i32
) + (chunk.position.0 << 4, (i << 4) as i32, chunk.position.1 << 4);
if chunk.block_entities.contains_key(&pos) {
self.block_entity_actions.push_back(BlockEntityAction::Remove(pos))
}
self.block_entity_actions.push_back(BlockEntityAction::Create(pos))
}
}
for entry in &section.block_map {
@ -675,6 +760,8 @@ pub struct Chunk {
heightmap: [u8; 16 * 16],
heightmap_dirty: bool,
block_entities: HashMap<Position, ecs::Entity, BuildHasherDefault<FNVHash>>,
}
impl Chunk {
@ -691,6 +778,7 @@ impl Chunk {
biomes: [0; 16 * 16],
heightmap: [0; 16 * 16],
heightmap_dirty: true,
block_entities: HashMap::with_hasher(BuildHasherDefault::default()),
}
}