Skip to content

Commit

Permalink
Merge pull request #7 from twetzel59/lights
Browse files Browse the repository at this point in the history
Lights
  • Loading branch information
twetzel59 authored Nov 5, 2017
2 parents 46c3bc9 + 54589a3 commit f8de626
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 11 deletions.
29 changes: 27 additions & 2 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use std::io::{Read, Write};
use std::net::{IpAddr, SocketAddr, TcpStream};
use std::sync::mpsc::Sender;
use std::thread;
use event::{BlockEvent, ChunkRequestEvent, Event, IdEvent, PositionEvent, SignEvent, TalkEvent};
use event::{BlockEvent, ChunkRequestEvent, Event, IdEvent, PositionEvent,
LightEvent, SignEvent, TalkEvent};
use server::ServerTime;
use world::{Block, chunked, Sign};
use world::{Block, chunked, Light, Sign};

/// A type representing the ID players are given to uniquely identify them on both the client
/// and the server side.
Expand Down Expand Up @@ -117,6 +118,11 @@ impl Client {
self.broadcast_block(((ev.x, ev.y, ev.z), &Block(ev.w)), (chunked(ev.x), chunked(ev.z)));
}

/// Tells the client that a light has changed.
pub fn send_light(&mut self, ev: &LightEvent) {
self.broadcast_light(((ev.x, ev.y, ev.z), &Light(ev.w)), (chunked(ev.x), chunked(ev.z)));
}

/// Notifies the client that another client has left.
pub fn send_disconnect(&mut self, other_id: Id) {
let msg = format!("D,{}\n", other_id);
Expand Down Expand Up @@ -196,6 +202,17 @@ impl Client {
// TODO: What if the stream is now closed? Alert something that client is disconnected.
let _ = self.send_stream.write_all(msg.as_bytes());
}

/// Sends a light update to the client.
pub fn broadcast_light(&mut self, light: ((i32, i32, i32), &Light), pq: (i32, i32)) {
let msg = format!("L,{},{},{},{},{},{}\n",
pq.0, pq.1,
(light.0).0, (light.0).1, (light.0).2,
(light.1).0);

// TODO: What if the stream is now closed? Alert something that client is disconnected.
let _ = self.send_stream.write_all(msg.as_bytes());
}
}

struct ClientThread {
Expand Down Expand Up @@ -318,6 +335,8 @@ impl ClientThread {
self.handle_chunk(payload);
} else if msg.starts_with('S') {
self.handle_sign(payload);
} else if msg.starts_with('L') {
self.handle_light(payload);
}
}

Expand Down Expand Up @@ -350,4 +369,10 @@ impl ClientThread {
self.tx.send(IdEvent { id: self.id, peer: self.addr, event: Event::Sign(ev) }).unwrap();
}
}

fn handle_light(&self, payload: &str) {
if let Ok(ev) = LightEvent::new(payload) {
self.tx.send(IdEvent { id: self.id, peer: self.addr, event: Event::Light(ev) }).unwrap();
}
}
}
50 changes: 50 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub enum Event {

/// Represents a sign added on a client.
Sign(SignEvent),

/// Represents a light toggled on a client.
Light(LightEvent),
}

/// Describes errors that occur parsing messages.
Expand Down Expand Up @@ -288,3 +291,50 @@ impl SignEvent {
println!("Warning: invalid sign packet.");
}
}

/// Corresponds to `L` light messages.
#[derive(Debug)]
pub struct LightEvent {
pub x: i32,
pub y: i32,
pub z: i32,
pub w: u8,
}

impl LightEvent {
/// Create a new light event information structure from an encoded payload.
pub fn new(payload: &str) -> Result<LightEvent, MessageParseError> {
let pieces: Vec<&str> = payload.split(|c| c == ',' || c == '\n').collect();

if pieces.len() != 4 {
Self::warn_invalid();
return Err(MessageParseError::InvalidLength);
}

match Self::parse_all(&pieces) {
Ok(v) => Ok(v),
Err(e) => {
Self::warn_invalid();
Err(MessageParseError::IntError(e))
},
}
}

fn parse_all(pieces: &Vec<&str>) -> Result<LightEvent, ParseIntError> {
let x = pieces[0].parse()?;
let y = pieces[1].parse()?;
let z = pieces[2].parse()?;
let w = pieces[3].parse()?;

Ok(LightEvent {
x,
y,
z,
w,
})
}

fn warn_invalid() {
println!("Warning: invalid light packet.");
}
}
35 changes: 33 additions & 2 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use std::time::{Duration, Instant};
use std::thread;
use client;
use commands::CommandHandler;
use event::{BlockEvent, ChunkRequestEvent, Event, IdEvent, PositionEvent, SignEvent, TalkEvent};
use event::{BlockEvent, ChunkRequestEvent, Event, IdEvent, PositionEvent,
LightEvent, SignEvent, TalkEvent};
use nick::NickManager;
use world::{Block, Sign, World};
use world::{Block, Light, Sign, World};

pub const DAY_LENGTH: u32 = 600;

Expand Down Expand Up @@ -145,6 +146,10 @@ impl EventThread {
println!("{:?}", s);
self.handle_sign_event(s);
}
Event::Light(l) => {
println!("{:?}", l);
self.handle_light_event(l);
}
}
}
}
Expand Down Expand Up @@ -267,6 +272,17 @@ impl EventThread {
}
}

if let Some(it) = self.world.lights_in_chunk((ev.p, ev.q)) {
for (xyz, w) in it {
let xyz = (xyz.0 as i32 + (ev.p * CHUNK_SIZE as i32) - 1,
xyz.1 as i32,
xyz.2 as i32 + (ev.q * CHUNK_SIZE as i32) - 1);
c.broadcast_light((xyz, w), (ev.p, ev.q));

redraw = true;
}
}

if redraw {
c.broadcast_redraw((ev.p, ev.q));
}
Expand All @@ -285,6 +301,21 @@ impl EventThread {

self.world.set_sign((ev.x, ev.y, ev.z), chunk, ev.face, Sign(ev.text));
}

fn handle_light_event(&mut self, ev: LightEvent) {
use world::chunked;

let (p, q) = (chunked(ev.x), chunked(ev.z));

let mut clients = self.clients.lock().unwrap();

for i in clients.values_mut() {
i.send_light(&ev);
i.broadcast_redraw((p, q));
}

self.world.set_light((ev.x, ev.y, ev.z), (p, q), Light(ev.w));
}
}

/// Stores the data needed to find the game time of day.
Expand Down
83 changes: 83 additions & 0 deletions src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,25 @@ impl Block {
#[derive(Clone, Debug)]
pub struct Sign(pub String);

/// Type of Craft lights.
#[derive(Clone, Debug)]
pub struct Light(pub u8);

/* /// A structure representing a sector of the world.
pub */
#[derive(Debug)]
struct Chunk {
blocks: HashMap<(u8, u8, u8), Block>,
signs: HashMap<(i32, i32, i32, u8), Sign>,
lights: HashMap<(u8, u8, u8), Light>,
}

impl Chunk {
fn new() -> Chunk {
Chunk {
blocks: HashMap::new(),
signs: HashMap::new(),
lights: HashMap::new(),
}
}

Expand All @@ -55,9 +61,17 @@ impl Chunk {
self.signs.insert(key, sign);
}

fn set_light(&mut self, local_pos: (u8, u8, u8), light: Light) {
self.lights.insert(local_pos, light);
}

fn signs(&self) -> hash_map::Iter<(i32, i32, i32, u8), Sign> {
self.signs.iter()
}

fn lights(&self) -> hash_map::Iter<(u8, u8, u8), Light> {
self.lights.iter()
}
}

impl<'a> IntoIterator for &'a Chunk {
Expand Down Expand Up @@ -127,6 +141,16 @@ impl ChunkManager {
//println!("all blocks and signs: {:?}", self.chunks);
}

fn set_light(&mut self, global_pos: (i32, i32, i32), pq: (i32, i32), light: Light) {
let local_pos = ((global_pos.0 - pq.0 * CHUNK_SIZE as i32 + 1) as u8,
global_pos.1 as u8,
(global_pos.2 - pq.1 * CHUNK_SIZE as i32 + 1) as u8);

let entry = self.chunks.entry(pq);
let chunk = entry.or_insert(Chunk::new());
chunk.set_light(local_pos, light);
}

fn get(&self, pq: (i32, i32)) -> Option<&Chunk> {
self.chunks.get(&pq)
}
Expand Down Expand Up @@ -158,6 +182,7 @@ impl World {
w.initial_queries(&conn);
w.load_blocks(&conn);
w.load_signs(&conn);
w.load_lights(&conn);

println!("OK");

Expand Down Expand Up @@ -187,6 +212,16 @@ impl World {
})).unwrap();
}

/// Set a light in the world using absolute world coordinates and chunk coordinates.
pub fn set_light(&mut self, global_pos: (i32, i32, i32), pq: (i32, i32), light: Light) {
self.chunk_mgr.set_light(global_pos, pq, light.clone());
self.tx.send(DatabaseCommand::SetLight(SetLightCommand {
xyz: global_pos,
pq,
light,
})).unwrap();
}

/// Iterate over the blocks in the chunk with these (P, Q) (as in (X, Z)) coordinates.
pub fn blocks_in_chunk(&self, chunk: (i32, i32)) -> Option<hash_map::Iter<(u8, u8, u8), Block>> {
match self.chunk_mgr.get(chunk) {
Expand All @@ -203,6 +238,14 @@ impl World {
}
}

/// Iterate over the lights in the chunk with these coordinates.
pub fn lights_in_chunk(&self, chunk: (i32, i32)) -> Option<hash_map::Iter<(u8, u8, u8), Light>> {
match self.chunk_mgr.get(chunk) {
Some(c) => Some(c.lights()),
None => None,
}
}

fn initial_queries(&self, conn: &Connection) {
conn.execute(queries::INITIAL).unwrap();
}
Expand Down Expand Up @@ -238,6 +281,21 @@ impl World {
self.chunk_mgr.set_sign(xyz, pq, face, Sign(text));
}
}

fn load_lights(&mut self, conn: &Connection) {
let mut cursor = conn.prepare(queries::LOAD_LIGHTS).unwrap().cursor();

while let Some(record) = cursor.next().unwrap() {
let (pq, xyz, w) = ((record[0].as_integer().unwrap() as i32,
record[1].as_integer().unwrap() as i32),
(record[2].as_integer().unwrap() as i32,
record[3].as_integer().unwrap() as i32,
record[4].as_integer().unwrap() as i32),
record[5].as_integer().unwrap() as u8);

self.chunk_mgr.set_light(xyz, pq, Light(w));
}
}
}

struct SetBlockCommand {
Expand All @@ -252,9 +310,16 @@ struct SetSignCommand {
pub sign: Sign,
}

struct SetLightCommand {
pub xyz: (i32, i32, i32),
pub pq: (i32, i32),
pub light: Light,
}

enum DatabaseCommand {
SetBlock(SetBlockCommand),
SetSign(SetSignCommand),
SetLight(SetLightCommand),
}

struct DatabaseThread<'l> {
Expand Down Expand Up @@ -288,6 +353,7 @@ impl<'l> DatabaseThread<'l> {
match cmd {
DatabaseCommand::SetBlock(c) => self.handle_set_block(&c),
DatabaseCommand::SetSign(c) => self.handle_set_sign(&c),
DatabaseCommand::SetLight(c) => self.handle_set_light(&c),
}
}

Expand Down Expand Up @@ -344,6 +410,17 @@ impl<'l> DatabaseThread<'l> {
s.0.bind(7, cmd.sign.0.deref()).unwrap();
}
}

fn handle_set_light(&mut self, cmd: &SetLightCommand) {
let s = self.statements.set_light();

s.0.bind(1, cmd.pq.0 as i64).unwrap();
s.0.bind(2, cmd.pq.1 as i64).unwrap();
s.0.bind(3, cmd.xyz.0 as i64).unwrap();
s.0.bind(4, cmd.xyz.1 as i64).unwrap();
s.0.bind(5, cmd.xyz.2 as i64).unwrap();
s.0.bind(6, cmd.light.0 as i64).unwrap();
}
}

/// Return the chunk that a block falls in on one axis.
Expand All @@ -356,6 +433,7 @@ struct PreparedStatements<'l> {
set_sign: Statement<'l>,
delete_individual_sign: Statement<'l>,
delete_signs: Statement<'l>,
set_light: Statement<'l>,
}

impl<'l> PreparedStatements<'l> {
Expand All @@ -365,6 +443,7 @@ impl<'l> PreparedStatements<'l> {
set_sign: conn.prepare(queries::SET_SIGN).unwrap(),
delete_individual_sign: conn.prepare(queries::DELETE_INDIVIDUAL_SIGN).unwrap(),
delete_signs: conn.prepare(queries::DELETE_SIGNS).unwrap(),
set_light: conn.prepare(queries::SET_LIGHT).unwrap(),
}
}

Expand All @@ -383,6 +462,10 @@ impl<'l> PreparedStatements<'l> {
fn delete_signs<'p>(&'p mut self) -> StatementWrapper<'l, 'p> {
StatementWrapper(&mut self.delete_signs)
}

fn set_light<'p>(&'p mut self) -> StatementWrapper<'l, 'p> {
StatementWrapper(&mut self.set_light)
}
}

struct StatementWrapper<'l, 'p>(&'p mut Statement<'l>) where 'l: 'p;
Expand Down
Loading

0 comments on commit f8de626

Please sign in to comment.