mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merged with UI-Input
Former-commit-id: f18e92dcac87c3b2806065078c70527ffd805876
This commit is contained in:
parent
6d6c585808
commit
6ebff7ce2c
@ -1,74 +0,0 @@
|
|||||||
#[derive(Debug)]
|
|
||||||
pub enum PostError {
|
|
||||||
InvalidMessage,
|
|
||||||
InternalError,
|
|
||||||
Disconnected,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum PostErrorInternal {
|
|
||||||
Io(std::io::Error),
|
|
||||||
Serde(bincode::Error),
|
|
||||||
ChannelRecv(std::sync::mpsc::TryRecvError),
|
|
||||||
ChannelSend, // Empty because I couldn't figure out how to handle generic type in mpsc::TrySendError properly
|
|
||||||
MsgSizeLimitExceeded,
|
|
||||||
MioError,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Into<&'a PostErrorInternal>> From<T> for PostError {
|
|
||||||
fn from(err: T) -> Self {
|
|
||||||
match err.into() {
|
|
||||||
// TODO: Are I/O errors always disconnect errors?
|
|
||||||
PostErrorInternal::Io(_) => PostError::Disconnected,
|
|
||||||
PostErrorInternal::Serde(_) => PostError::InvalidMessage,
|
|
||||||
PostErrorInternal::MsgSizeLimitExceeded => PostError::InvalidMessage,
|
|
||||||
PostErrorInternal::MioError => PostError::InternalError,
|
|
||||||
PostErrorInternal::ChannelRecv(_) => PostError::InternalError,
|
|
||||||
PostErrorInternal::ChannelSend => PostError::InternalError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PostErrorInternal> for PostError {
|
|
||||||
fn from(err: PostErrorInternal) -> Self {
|
|
||||||
(&err).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for PostErrorInternal {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
PostErrorInternal::Io(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bincode::Error> for PostErrorInternal {
|
|
||||||
fn from(err: bincode::Error) -> Self {
|
|
||||||
PostErrorInternal::Serde(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::sync::mpsc::TryRecvError> for PostErrorInternal {
|
|
||||||
fn from(err: std::sync::mpsc::TryRecvError) -> Self {
|
|
||||||
PostErrorInternal::ChannelRecv(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl From<std::io::Error> for PostError {
|
|
||||||
fn from(err: std::io::Error) -> Self {
|
|
||||||
(&PostErrorInternal::from(err)).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bincode::Error> for PostError {
|
|
||||||
fn from(err: bincode::Error) -> Self {
|
|
||||||
(&PostErrorInternal::from(err)).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::sync::mpsc::TryRecvError> for PostError {
|
|
||||||
fn from(err: std::sync::mpsc::TryRecvError) -> Self {
|
|
||||||
(&PostErrorInternal::from(err)).into()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,5 @@
|
|||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod error;
|
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod postbox;
|
|
||||||
pub mod postoffice;
|
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::{
|
pub use self::{
|
||||||
|
@ -116,7 +116,7 @@ impl<S: PostSend, R: PostRecv> PostOffice<S, R> {
|
|||||||
pub fn new_connections(&mut self) -> impl ExactSizeIterator<Item=PostBox<S, R>> {
|
pub fn new_connections(&mut self) -> impl ExactSizeIterator<Item=PostBox<S, R>> {
|
||||||
let mut conns = VecDeque::new();
|
let mut conns = VecDeque::new();
|
||||||
|
|
||||||
if let Some(_) = self.err {
|
if self.err.is_some() {
|
||||||
return conns.into_iter();
|
return conns.into_iter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ impl<S: PostSend, R: PostRecv> PostOffice<S, R> {
|
|||||||
for event in events {
|
for event in events {
|
||||||
match event.token() {
|
match event.token() {
|
||||||
// Keep reading new postboxes from the channel
|
// Keep reading new postboxes from the channel
|
||||||
POSTBOX_TOKEN => loop {
|
POSTBOX_TOK => loop {
|
||||||
match self.postbox_rx.try_recv() {
|
match self.postbox_rx.try_recv() {
|
||||||
Ok(Ok(conn)) => conns.push_back(conn),
|
Ok(Ok(conn)) => conns.push_back(conn),
|
||||||
Err(TryRecvError::Empty) => break,
|
Err(TryRecvError::Empty) => break,
|
||||||
@ -141,7 +141,7 @@ impl<S: PostSend, R: PostRecv> PostOffice<S, R> {
|
|||||||
return conns.into_iter();
|
return conns.into_iter();
|
||||||
},
|
},
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
self.err = Some(err.into());
|
self.err = Some(err);
|
||||||
return conns.into_iter();
|
return conns.into_iter();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ pub struct PostBox<S: PostSend, R: PostRecv> {
|
|||||||
|
|
||||||
impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
||||||
pub fn to_server<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
|
pub fn to_server<A: Into<SocketAddr>>(addr: A) -> Result<Self, Error> {
|
||||||
Self::from_tcpstream(TcpStream::connect(&addr.into())?)
|
Self::from_tcpstream(TcpStream::from_stream(std::net::TcpStream::connect(&addr.into())?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_tcpstream(tcp_stream: TcpStream) -> Result<Self, Error> {
|
fn from_tcpstream(tcp_stream: TcpStream) -> Result<Self, Error> {
|
||||||
@ -254,7 +254,7 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
|||||||
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
|
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item=R> {
|
||||||
let mut msgs = VecDeque::new();
|
let mut msgs = VecDeque::new();
|
||||||
|
|
||||||
if let Some(_) = self.err {
|
if self.err.is_some() {
|
||||||
return msgs.into_iter();
|
return msgs.into_iter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
|||||||
for event in events {
|
for event in events {
|
||||||
match event.token() {
|
match event.token() {
|
||||||
// Keep reading new messages from the channel
|
// Keep reading new messages from the channel
|
||||||
RECV_TOKEN => loop {
|
RECV_TOK => loop {
|
||||||
match self.recv_rx.try_recv() {
|
match self.recv_rx.try_recv() {
|
||||||
Ok(Ok(msg)) => msgs.push_back(msg),
|
Ok(Ok(msg)) => msgs.push_back(msg),
|
||||||
Err(TryRecvError::Empty) => break,
|
Err(TryRecvError::Empty) => break,
|
||||||
@ -279,7 +279,7 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
|||||||
return msgs.into_iter();
|
return msgs.into_iter();
|
||||||
},
|
},
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
self.err = Some(err.into());
|
self.err = Some(err);
|
||||||
return msgs.into_iter();
|
return msgs.into_iter();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -322,17 +322,16 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
|
|
||||||
for event in &events {
|
for event in &events {
|
||||||
match event.token() {
|
match event.token() {
|
||||||
CTRL_TOK => loop {
|
CTRL_TOK =>
|
||||||
match ctrl_rx.try_recv() {
|
match ctrl_rx.try_recv() {
|
||||||
Ok(CtrlMsg::Shutdown) => {
|
Ok(CtrlMsg::Shutdown) => {
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
Err(TryRecvError::Empty) => break,
|
Err(TryRecvError::Empty) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
recv_tx.send(Err(err.into()))?;
|
recv_tx.send(Err(err.into()))?;
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
SEND_TOK => loop {
|
SEND_TOK => loop {
|
||||||
match send_rx.try_recv() {
|
match send_rx.try_recv() {
|
||||||
@ -340,7 +339,7 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
let mut msg_bytes = match bincode::serialize(&outgoing_msg) {
|
let mut msg_bytes = match bincode::serialize(&outgoing_msg) {
|
||||||
Ok(bytes) => bytes,
|
Ok(bytes) => bytes,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
recv_tx.send(Err((*err).into()));
|
recv_tx.send(Err((*err).into()))?;
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -355,7 +354,7 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
match tcp_stream.write_all(&packet) {
|
match tcp_stream.write_all(&packet) {
|
||||||
Ok(()) => {},
|
Ok(()) => {},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
recv_tx.send(Err(err.into()));
|
recv_tx.send(Err(err.into()))?;
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -368,11 +367,11 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
match tcp_stream.take_error() {
|
match tcp_stream.take_error() {
|
||||||
Ok(None) => {},
|
Ok(None) => {},
|
||||||
Ok(Some(err)) => {
|
Ok(Some(err)) => {
|
||||||
recv_tx.send(Err(err.into()));
|
recv_tx.send(Err(err.into()))?;
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
recv_tx.send(Err(err.into()));
|
recv_tx.send(Err(err.into()))?;
|
||||||
break 'work;
|
break 'work;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -380,7 +379,7 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
RecvState::ReadHead(head) => if head.len() == 8 {
|
RecvState::ReadHead(head) => if head.len() == 8 {
|
||||||
let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap());
|
let len = usize::from_le_bytes(<[u8; 8]>::try_from(head.as_slice()).unwrap());
|
||||||
if len > MAX_MSG_BYTES {
|
if len > MAX_MSG_BYTES {
|
||||||
recv_tx.send(Err(Error::InvalidMsg));
|
recv_tx.send(Err(Error::InvalidMsg))?;
|
||||||
break 'work;
|
break 'work;
|
||||||
} else if len == 0 {
|
} else if len == 0 {
|
||||||
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
recv_state = RecvState::ReadHead(Vec::with_capacity(8));
|
||||||
@ -427,7 +426,7 @@ fn postbox_worker<S: PostSend, R: PostRecv>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_stream.shutdown(Shutdown::Both);
|
tcp_stream.shutdown(Shutdown::Both)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +452,21 @@ fn connect() {
|
|||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn connect_fail() {
|
||||||
|
let listen_addr = ([0; 4], 12345);
|
||||||
|
let connect_addr = ([127, 0, 0, 1], 12212);
|
||||||
|
|
||||||
|
let mut postoffice = PostOffice::<u32, f32>::bind(listen_addr).unwrap();
|
||||||
|
|
||||||
|
// We should start off with 0 incoming connections
|
||||||
|
thread::sleep(Duration::from_millis(250));
|
||||||
|
assert_eq!(postoffice.new_connections().len(), 0);
|
||||||
|
assert_eq!(postoffice.error(), None);
|
||||||
|
|
||||||
|
assert!(PostBox::<f32, u32>::to_server(connect_addr).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn connection_count() {
|
fn connection_count() {
|
||||||
let srv_addr = ([127, 0, 0, 1], 12346);
|
let srv_addr = ([127, 0, 0, 1], 12346);
|
||||||
@ -469,7 +483,7 @@ fn connection_count() {
|
|||||||
postboxes.push(PostBox::<f32, u32>::to_server(srv_addr).unwrap());
|
postboxes.push(PostBox::<f32, u32>::to_server(srv_addr).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10 postboxes created, we should have 10
|
// 5 postboxes created, we should have 5
|
||||||
thread::sleep(Duration::from_millis(3500));
|
thread::sleep(Duration::from_millis(3500));
|
||||||
let incoming = postoffice.new_connections();
|
let incoming = postoffice.new_connections();
|
||||||
assert_eq!(incoming.len(), 5);
|
assert_eq!(incoming.len(), 5);
|
||||||
|
@ -1,270 +0,0 @@
|
|||||||
// Standard
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
convert::TryFrom,
|
|
||||||
io::{
|
|
||||||
ErrorKind,
|
|
||||||
Read,
|
|
||||||
},
|
|
||||||
net::SocketAddr,
|
|
||||||
thread,
|
|
||||||
time::Duration,
|
|
||||||
sync::mpsc::TryRecvError,
|
|
||||||
};
|
|
||||||
|
|
||||||
// External
|
|
||||||
use bincode;
|
|
||||||
use mio::{net::TcpStream, Events, Poll, PollOpt, Ready, Token};
|
|
||||||
use mio_extras::channel::{channel, Receiver, Sender};
|
|
||||||
|
|
||||||
// Crate
|
|
||||||
use super::{
|
|
||||||
data::ControlMsg,
|
|
||||||
error::{
|
|
||||||
PostError,
|
|
||||||
PostErrorInternal,
|
|
||||||
},
|
|
||||||
PostRecv,
|
|
||||||
PostSend,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const CTRL_TOKEN: Token = Token(0); // Token for thread control messages
|
|
||||||
const DATA_TOKEN: Token = Token(1); // Token for thread data exchange
|
|
||||||
const CONN_TOKEN: Token = Token(2); // Token for TcpStream for the PostBox child thread
|
|
||||||
const MESSAGE_SIZE_CAP: u64 = 1 << 20; // Maximum accepted length of a packet
|
|
||||||
|
|
||||||
/// A high-level wrapper of [`TcpStream`](mio::net::TcpStream).
|
|
||||||
/// [`PostBox`] takes care of serializing sent packets and deserializing received packets in the background, providing a simple API for sending and receiving objects over network.
|
|
||||||
pub struct PostBox<S, R>
|
|
||||||
where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
handle: Option<thread::JoinHandle<()>>,
|
|
||||||
ctrl: Sender<ControlMsg>,
|
|
||||||
recv: Receiver<Result<R, PostErrorInternal>>,
|
|
||||||
send: Sender<S>,
|
|
||||||
poll: Poll,
|
|
||||||
err: Option<PostErrorInternal>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, R> PostBox<S, R>
|
|
||||||
where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
/// Creates a new [`PostBox`] connected to specified address, meant to be used by the client
|
|
||||||
pub fn to_server<A: Into<SocketAddr>>(addr: A) -> Result<PostBox<S, R>, PostError> {
|
|
||||||
let connection = TcpStream::connect(&addr.into())?;
|
|
||||||
Self::from_tcpstream(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`PostBox`] from an existing connection, meant to be used by [`PostOffice`](super::PostOffice) on the server
|
|
||||||
pub fn from_tcpstream(connection: TcpStream) -> Result<PostBox<S, R>, PostError> {
|
|
||||||
let (ctrl_tx, ctrl_rx) = channel(); // Control messages
|
|
||||||
let (send_tx, send_rx) = channel(); // main thread -[data to be serialized and sent]> worker thread
|
|
||||||
let (recv_tx, recv_rx) = channel(); // main thread <[received and deserialized data]- worker thread
|
|
||||||
let thread_poll = Poll::new().unwrap();
|
|
||||||
let postbox_poll = Poll::new().unwrap();
|
|
||||||
thread_poll
|
|
||||||
.register(&connection, CONN_TOKEN, Ready::readable(), PollOpt::edge())
|
|
||||||
.unwrap();
|
|
||||||
thread_poll
|
|
||||||
.register(&ctrl_rx, CTRL_TOKEN, Ready::readable(), PollOpt::edge())
|
|
||||||
.unwrap();
|
|
||||||
thread_poll
|
|
||||||
.register(&send_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())
|
|
||||||
.unwrap();
|
|
||||||
postbox_poll
|
|
||||||
.register(&recv_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())
|
|
||||||
.unwrap();
|
|
||||||
let handle = thread::Builder::new()
|
|
||||||
.name("postbox_worker".into())
|
|
||||||
.spawn(move || postbox_thread(connection, ctrl_rx, send_rx, recv_tx, thread_poll))?;
|
|
||||||
Ok(PostBox {
|
|
||||||
handle: Some(handle),
|
|
||||||
ctrl: ctrl_tx,
|
|
||||||
recv: recv_rx,
|
|
||||||
send: send_tx,
|
|
||||||
poll: postbox_poll,
|
|
||||||
err: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an `Option<PostError>` indicating the current status of the `PostBox`.
|
|
||||||
pub fn status(&self) -> Option<PostError> {
|
|
||||||
self.err.as_ref().map(|err| err.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Non-blocking sender method
|
|
||||||
pub fn send(&mut self, data: S) -> Result<(), PostError> {
|
|
||||||
match &mut self.err {
|
|
||||||
err @ None => if let Err(_) = self.send.send(data) {
|
|
||||||
*err = Some(PostErrorInternal::MioError);
|
|
||||||
Err(err.as_ref().unwrap().into())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
err => Err(err.as_ref().unwrap().into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Non-blocking receiver method returning an iterator over already received and deserialized objects
|
|
||||||
/// # Errors
|
|
||||||
/// If the other side disconnects PostBox won't realize that until you try to send something
|
|
||||||
pub fn new_messages(&mut self) -> impl ExactSizeIterator<Item = R> {
|
|
||||||
let mut events = Events::with_capacity(4096);
|
|
||||||
let mut items = VecDeque::new();
|
|
||||||
|
|
||||||
// If an error occured, or previously occured, just give up
|
|
||||||
if let Some(_) = self.err {
|
|
||||||
return items.into_iter();
|
|
||||||
} else if let Err(err) = self.poll.poll(&mut events, Some(Duration::new(0, 0))) {
|
|
||||||
self.err = Some(err.into());
|
|
||||||
return items.into_iter();
|
|
||||||
}
|
|
||||||
|
|
||||||
for event in events {
|
|
||||||
match event.token() {
|
|
||||||
DATA_TOKEN => loop {
|
|
||||||
match self.recv.try_recv() {
|
|
||||||
Ok(Ok(item)) => items.push_back(item),
|
|
||||||
Err(TryRecvError::Empty) => break,
|
|
||||||
Err(err) => self.err = Some(err.into()),
|
|
||||||
Ok(Err(err)) => self.err = Some(err.into()),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
items.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postbox_thread<S, R>(
|
|
||||||
mut connection: TcpStream,
|
|
||||||
ctrl_rx: Receiver<ControlMsg>,
|
|
||||||
send_rx: Receiver<S>,
|
|
||||||
recv_tx: Sender<Result<R, PostErrorInternal>>,
|
|
||||||
poll: Poll,
|
|
||||||
) where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
// Receiving related variables
|
|
||||||
let mut events = Events::with_capacity(64);
|
|
||||||
let mut recv_buff = Vec::new();
|
|
||||||
let mut recv_nextlen: u64 = 0;
|
|
||||||
loop {
|
|
||||||
let mut disconnected = false;
|
|
||||||
poll.poll(&mut events, Some(Duration::from_millis(20)))
|
|
||||||
.expect("Failed to execute poll(), most likely fault of the OS");
|
|
||||||
println!("FINISHED POLL!");
|
|
||||||
for event in events.iter() {
|
|
||||||
println!("EVENT!");
|
|
||||||
match event.token() {
|
|
||||||
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
|
|
||||||
ControlMsg::Shutdown => return,
|
|
||||||
},
|
|
||||||
CONN_TOKEN => match connection.read_to_end(&mut recv_buff) {
|
|
||||||
Ok(_) => {}
|
|
||||||
// Returned when all the data has been read
|
|
||||||
Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
|
|
||||||
Err(e) => recv_tx.send(Err(e.into())).unwrap(),
|
|
||||||
},
|
|
||||||
DATA_TOKEN => {
|
|
||||||
let msg = send_rx.try_recv().unwrap();
|
|
||||||
println!("Send: {:?}", msg);
|
|
||||||
let mut packet = bincode::serialize(&msg).unwrap();
|
|
||||||
packet.splice(0..0, (packet.len() as u64).to_be_bytes().iter().cloned());
|
|
||||||
match connection.write_bufs(&[packet.as_slice().into()]) {
|
|
||||||
Ok(_) => { println!("Sent!"); }
|
|
||||||
Err(e) => {
|
|
||||||
println!("Send error!");
|
|
||||||
recv_tx.send(Err(e.into())).unwrap();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
if recv_nextlen == 0 && recv_buff.len() >= 8 {
|
|
||||||
println!("Read nextlen");
|
|
||||||
recv_nextlen = u64::from_be_bytes(
|
|
||||||
<[u8; 8]>::try_from(recv_buff.drain(0..8).collect::<Vec<u8>>().as_slice()).unwrap(),
|
|
||||||
);
|
|
||||||
if recv_nextlen > MESSAGE_SIZE_CAP {
|
|
||||||
recv_tx.send(Err(PostErrorInternal::MsgSizeLimitExceeded)).unwrap();
|
|
||||||
connection.shutdown(std::net::Shutdown::Both).unwrap();
|
|
||||||
recv_buff.drain(..);
|
|
||||||
recv_nextlen = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if recv_buff.len() as u64 >= recv_nextlen && recv_nextlen != 0 {
|
|
||||||
match bincode::deserialize(recv_buff
|
|
||||||
.drain(
|
|
||||||
0..usize::try_from(recv_nextlen)
|
|
||||||
.expect("Message size was larger than usize (insane message size and 32 bit OS)"),
|
|
||||||
)
|
|
||||||
.collect::<Vec<u8>>()
|
|
||||||
.as_slice()) {
|
|
||||||
Ok(msg) => {
|
|
||||||
println!("Recv: {:?}", msg);
|
|
||||||
recv_tx
|
|
||||||
.send(Ok(msg))
|
|
||||||
.unwrap();
|
|
||||||
recv_nextlen = 0;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Recv error: {:?}", e);
|
|
||||||
recv_tx.send(Err(e.into())).unwrap();
|
|
||||||
recv_nextlen = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match connection.take_error().unwrap() {
|
|
||||||
Some(e) => {
|
|
||||||
if e.kind() == ErrorKind::BrokenPipe {
|
|
||||||
disconnected = true;
|
|
||||||
}
|
|
||||||
recv_tx.send(Err(e.into())).unwrap();
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
if disconnected == true {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop after disconnected
|
|
||||||
loop {
|
|
||||||
poll.poll(&mut events, None)
|
|
||||||
.expect("Failed to execute poll(), most likely fault of the OS");
|
|
||||||
for event in events.iter() {
|
|
||||||
match event.token() {
|
|
||||||
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
|
|
||||||
ControlMsg::Shutdown => return,
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, R> Drop for PostBox<S, R>
|
|
||||||
where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.ctrl.send(ControlMsg::Shutdown).unwrap_or(());
|
|
||||||
self.handle.take().map(|handle| handle.join());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
// Standard
|
|
||||||
use core::time::Duration;
|
|
||||||
use std::{
|
|
||||||
collections::VecDeque,
|
|
||||||
net::SocketAddr,
|
|
||||||
thread,
|
|
||||||
sync::mpsc::TryRecvError,
|
|
||||||
};
|
|
||||||
|
|
||||||
// External
|
|
||||||
use mio::{net::TcpListener, Events, Poll, PollOpt, Ready, Token};
|
|
||||||
use mio_extras::channel::{channel, Receiver, Sender};
|
|
||||||
|
|
||||||
// Crate
|
|
||||||
use super::{
|
|
||||||
data::ControlMsg,
|
|
||||||
error::{
|
|
||||||
PostError,
|
|
||||||
PostErrorInternal,
|
|
||||||
},
|
|
||||||
postbox::PostBox,
|
|
||||||
PostRecv,
|
|
||||||
PostSend,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const CTRL_TOKEN: Token = Token(0); // Token for thread control messages
|
|
||||||
const DATA_TOKEN: Token = Token(1); // Token for thread data exchange
|
|
||||||
const CONN_TOKEN: Token = Token(2); // Token for TcpStream for the PostBox child thread
|
|
||||||
|
|
||||||
/// A high-level wrapper of [`TcpListener`](mio::net::TcpListener).
|
|
||||||
/// [`PostOffice`] listens for incoming connections in the background and wraps them into [`PostBox`]es, providing a simple non-blocking API for receiving them.
|
|
||||||
pub struct PostOffice<S, R>
|
|
||||||
where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
handle: Option<thread::JoinHandle<()>>,
|
|
||||||
ctrl: Sender<ControlMsg>,
|
|
||||||
recv: Receiver<Result<PostBox<S, R>, PostErrorInternal>>,
|
|
||||||
poll: Poll,
|
|
||||||
err: Option<PostErrorInternal>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, R> PostOffice<S, R>
|
|
||||||
where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
/// Creates a new [`PostOffice`] listening on specified address
|
|
||||||
pub fn new<A: Into<SocketAddr>>(addr: A) -> Result<Self, PostError> {
|
|
||||||
let listener = TcpListener::bind(&addr.into())?;
|
|
||||||
let (ctrl_tx, ctrl_rx) = channel();
|
|
||||||
let (recv_tx, recv_rx) = channel();
|
|
||||||
|
|
||||||
let thread_poll = Poll::new()?;
|
|
||||||
let postbox_poll = Poll::new()?;
|
|
||||||
thread_poll.register(&listener, CONN_TOKEN, Ready::readable(), PollOpt::edge())?;
|
|
||||||
thread_poll.register(&ctrl_rx, CTRL_TOKEN, Ready::readable(), PollOpt::edge())?;
|
|
||||||
postbox_poll.register(&recv_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())?;
|
|
||||||
|
|
||||||
let handle = thread::Builder::new()
|
|
||||||
.name("postoffice_worker".into())
|
|
||||||
.spawn(move || postoffice_thread(listener, ctrl_rx, recv_tx, thread_poll))?;
|
|
||||||
|
|
||||||
Ok(PostOffice {
|
|
||||||
handle: Some(handle),
|
|
||||||
ctrl: ctrl_tx,
|
|
||||||
recv: recv_rx,
|
|
||||||
poll: postbox_poll,
|
|
||||||
err: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return an `Option<PostError>` indicating the current status of the `PostOffice`.
|
|
||||||
pub fn status(&self) -> Option<PostError> {
|
|
||||||
self.err.as_ref().map(|err| err.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Non-blocking method returning an iterator over new connections wrapped in [`PostBox`]es
|
|
||||||
pub fn new_connections(
|
|
||||||
&mut self,
|
|
||||||
) -> impl ExactSizeIterator<Item = PostBox<S, R>> {
|
|
||||||
let mut events = Events::with_capacity(256);
|
|
||||||
let mut conns = VecDeque::new();
|
|
||||||
|
|
||||||
// If an error occured, or previously occured, just give up
|
|
||||||
if let Some(_) = self.err {
|
|
||||||
return conns.into_iter();
|
|
||||||
} else if let Err(err) = self.poll.poll(&mut events, Some(Duration::new(0, 0))) {
|
|
||||||
self.err = Some(err.into());
|
|
||||||
return conns.into_iter();
|
|
||||||
}
|
|
||||||
|
|
||||||
for event in events {
|
|
||||||
match event.token() {
|
|
||||||
DATA_TOKEN => loop {
|
|
||||||
match self.recv.try_recv() {
|
|
||||||
Ok(Ok(conn)) => conns.push_back(conn),
|
|
||||||
Err(TryRecvError::Empty) => break,
|
|
||||||
Err(err) => self.err = Some(err.into()),
|
|
||||||
Ok(Err(err)) => self.err = Some(err.into()),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conns.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postoffice_thread<S, R>(
|
|
||||||
listener: TcpListener,
|
|
||||||
ctrl_rx: Receiver<ControlMsg>,
|
|
||||||
recv_tx: Sender<Result<PostBox<S, R>, PostErrorInternal>>,
|
|
||||||
poll: Poll,
|
|
||||||
) where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
let mut events = Events::with_capacity(256);
|
|
||||||
loop {
|
|
||||||
poll.poll(&mut events, None).expect("Failed to execute recv_poll.poll() in PostOffce receiver thread, most likely fault of the OS.");
|
|
||||||
for event in events.iter() {
|
|
||||||
match event.token() {
|
|
||||||
CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() {
|
|
||||||
ControlMsg::Shutdown => return,
|
|
||||||
},
|
|
||||||
CONN_TOKEN => {
|
|
||||||
let (conn, _addr) = listener.accept().unwrap();
|
|
||||||
recv_tx.send(PostBox::from_tcpstream(conn)
|
|
||||||
// TODO: Is it okay to count a failure to create a postbox here as an 'internal error'?
|
|
||||||
.map_err(|_| PostErrorInternal::MioError)).unwrap();
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, R> Drop for PostOffice<S, R>
|
|
||||||
where
|
|
||||||
S: PostSend,
|
|
||||||
R: PostRecv,
|
|
||||||
{
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.ctrl.send(ControlMsg::Shutdown).unwrap_or(()); // If this fails the thread is dead already
|
|
||||||
self.handle.take().map(|handle| handle.join());
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ use conrod_core::{
|
|||||||
input::Key,
|
input::Key,
|
||||||
position::Dimension,
|
position::Dimension,
|
||||||
text::font::Id as FontId,
|
text::font::Id as FontId,
|
||||||
widget::{Button, Id, List, Rectangle, Text, TextEdit},
|
widget::{Id, Button, List, Rectangle, Text, TextEdit},
|
||||||
widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget,
|
widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget,
|
||||||
};
|
};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
@ -15,8 +15,6 @@ widget_ids! {
|
|||||||
input,
|
input,
|
||||||
input_bg,
|
input_bg,
|
||||||
chat_arrow,
|
chat_arrow,
|
||||||
chat_arrow_up,
|
|
||||||
chat_arrow_down,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Chat Behaviour:
|
// Chat Behaviour:
|
||||||
@ -78,12 +76,7 @@ impl Chat {
|
|||||||
fn scroll_to_bottom(&self, ui_widgets: &mut UiCell) {
|
fn scroll_to_bottom(&self, ui_widgets: &mut UiCell) {
|
||||||
ui_widgets.scroll_widget(self.ids.message_box, [0.0, std::f64::MAX]);
|
ui_widgets.scroll_widget(self.ids.message_box, [0.0, std::f64::MAX]);
|
||||||
}
|
}
|
||||||
pub fn update_layout(
|
pub(super) fn update_layout(&mut self, ui_widgets: &mut UiCell, font: FontId, imgs: &super::Imgs) -> Option<String> {
|
||||||
&mut self,
|
|
||||||
ui_widgets: &mut UiCell,
|
|
||||||
font: FontId,
|
|
||||||
imgs: &super::Imgs,
|
|
||||||
) -> Option<String> {
|
|
||||||
// Maintain scrolling
|
// Maintain scrolling
|
||||||
if self.new_messages {
|
if self.new_messages {
|
||||||
self.scroll_new_messages(ui_widgets);
|
self.scroll_new_messages(ui_widgets);
|
||||||
@ -138,39 +131,20 @@ impl Chat {
|
|||||||
// s.set(ui_widgets)
|
// s.set(ui_widgets)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// Chat Arrows
|
// Chat Arrow
|
||||||
if !self.scrolled_to_bottom(ui_widgets) {
|
if !self.scrolled_to_bottom(ui_widgets) {
|
||||||
if Button::image(imgs.chat_arrow)
|
if Button::image(imgs.chat_arrow)
|
||||||
.w_h(22.0, 22.0)
|
.w_h(22.0, 22.0)
|
||||||
.hover_image(imgs.chat_arrow_mo)
|
.hover_image(imgs.chat_arrow_mo)
|
||||||
.press_image(imgs.chat_arrow_press)
|
.press_image(imgs.chat_arrow_press)
|
||||||
.bottom_right_with_margins_on(self.ids.message_box_bg, 2.0, 2.0)
|
.bottom_right_with_margins_on(self.ids.message_box_bg, 2.0, 2.0)
|
||||||
.set(self.ids.chat_arrow, ui_widgets)
|
.set(self.ids.chat_arrow, ui_widgets)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
self.scroll_to_bottom(ui_widgets);
|
self.scroll_to_bottom(ui_widgets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up and Down Arrows => Scroll the chat up/down one row per click;
|
|
||||||
//if Button::image(imgs.chat_arrow_up)
|
|
||||||
//.w_h(22.0, 22.0)
|
|
||||||
//.hover_image(imgs.chat_arrow_up_mo)
|
|
||||||
//.press_image(imgs.chat_arrow_up_press)
|
|
||||||
//.up_from(self.ids.chat_arrow_down, 60.0)
|
|
||||||
//.set(self.ids.chat_arrow_up, ui_widgets)
|
|
||||||
//.was_clicked()
|
|
||||||
//{};
|
|
||||||
|
|
||||||
//if Button::image(imgs.chat_arrow_down)
|
|
||||||
// .w_h(22.0, 22.0)
|
|
||||||
//.hover_image(imgs.chat_arrow_down_mo)
|
|
||||||
//.press_image(imgs.chat_arrow_down_press)
|
|
||||||
//.bottom_right_with_margins_on(self.ids.message_box_bg, 73.0, 2.0)
|
|
||||||
// .set(self.ids.chat_arrow_down, ui_widgets)
|
|
||||||
//.was_clicked()
|
|
||||||
//{};
|
|
||||||
|
|
||||||
// If enter is pressed send the current message
|
// If enter is pressed send the current message
|
||||||
if ui_widgets
|
if ui_widgets
|
||||||
.widget_input(self.ids.input)
|
.widget_input(self.ids.input)
|
||||||
|
@ -6,8 +6,7 @@ use crate::{
|
|||||||
window::{Event as WinEvent, Key, Window},
|
window::{Event as WinEvent, Key, Window},
|
||||||
};
|
};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
event::Input,
|
|
||||||
image::Id as ImgId,
|
image::Id as ImgId,
|
||||||
text::font::Id as FontId,
|
text::font::Id as FontId,
|
||||||
widget::{Button, Image, Rectangle, Scrollbar, Text},
|
widget::{Button, Image, Rectangle, Scrollbar, Text},
|
||||||
@ -123,7 +122,7 @@ widget_ids! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make macro to mimic widget_ids! for images ids or find another solution to simplify addition of new images.
|
// TODO: make macro to mimic widget_ids! for images ids or find another solution to simplify addition of new images.
|
||||||
struct Imgs {
|
pub(self) struct Imgs {
|
||||||
//Missing: ActionBar, Health/Mana/Energy Bar & Char Window BG/Frame
|
//Missing: ActionBar, Health/Mana/Energy Bar & Char Window BG/Frame
|
||||||
// Bag
|
// Bag
|
||||||
bag: ImgId,
|
bag: ImgId,
|
||||||
|
@ -5,8 +5,9 @@ use crate::{
|
|||||||
session::SessionState,
|
session::SessionState,
|
||||||
GlobalState, PlayState, PlayStateResult,
|
GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
|
use client::{self, Client};
|
||||||
use common::clock::Clock;
|
use common::clock::Clock;
|
||||||
use std::time::Duration;
|
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||||
use ui::CharSelectionUi;
|
use ui::CharSelectionUi;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -14,13 +15,15 @@ const FPS: u64 = 60;
|
|||||||
|
|
||||||
pub struct CharSelectionState {
|
pub struct CharSelectionState {
|
||||||
char_selection_ui: CharSelectionUi,
|
char_selection_ui: CharSelectionUi,
|
||||||
|
client: Rc<RefCell<Client>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharSelectionState {
|
impl CharSelectionState {
|
||||||
/// Create a new `CharSelectionState`
|
/// Create a new `CharSelectionState`
|
||||||
pub fn new(window: &mut Window) -> Self {
|
pub fn new(window: &mut Window, client: Rc<RefCell<Client>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
char_selection_ui: CharSelectionUi::new(window),
|
char_selection_ui: CharSelectionUi::new(window),
|
||||||
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,7 +62,7 @@ impl PlayState for CharSelectionState {
|
|||||||
match event {
|
match event {
|
||||||
ui::Event::Logout => return PlayStateResult::Pop,
|
ui::Event::Logout => return PlayStateResult::Pop,
|
||||||
ui::Event::Play => return PlayStateResult::Push(
|
ui::Event::Play => return PlayStateResult::Push(
|
||||||
Box::new(SessionState::new(&mut global_state.window).unwrap()) // TODO: Handle this error
|
Box::new(SessionState::new(&mut global_state.window, self.client.clone()))
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +70,11 @@ impl PlayState for CharSelectionState {
|
|||||||
// Draw the UI to the screen
|
// Draw the UI to the screen
|
||||||
self.char_selection_ui.render(global_state.window.renderer_mut());
|
self.char_selection_ui.render(global_state.window.renderer_mut());
|
||||||
|
|
||||||
|
// Tick the client (currently only to keep the connection alive)
|
||||||
|
self.client.borrow_mut().tick(client::Input::default(), clock.get_last_delta())
|
||||||
|
.expect("Failed to tick the client");
|
||||||
|
self.client.borrow_mut().cleanup();
|
||||||
|
|
||||||
// Finish the frame
|
// Finish the frame
|
||||||
global_state.window.renderer_mut().flush();
|
global_state.window.renderer_mut().flush();
|
||||||
global_state
|
global_state
|
||||||
|
@ -6,10 +6,9 @@ use crate::{
|
|||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
color::TRANSPARENT,
|
color::TRANSPARENT,
|
||||||
event::Input,
|
|
||||||
image::Id as ImgId,
|
image::Id as ImgId,
|
||||||
text::font::Id as FontId,
|
text::font::Id as FontId,
|
||||||
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox, TitleBar},
|
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox},
|
||||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -224,7 +223,12 @@ impl Imgs {
|
|||||||
fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs {
|
fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs {
|
||||||
let mut load = |filename| {
|
let mut load = |filename| {
|
||||||
let image = image::open(
|
let image = image::open(
|
||||||
&[env!("CARGO_MANIFEST_DIR"), "/../assets/voxygen/", filename].concat(),
|
&[
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../assets/voxygen/",
|
||||||
|
filename,
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ui.new_image(renderer, &image).unwrap()
|
ui.new_image(renderer, &image).unwrap()
|
||||||
@ -850,18 +854,14 @@ impl CharSelectionUi {
|
|||||||
const HUMAN_DESC: &str =
|
const HUMAN_DESC: &str =
|
||||||
"The former nomads were only recently able to gain a foothold in the world of Veloren. \n\
|
"The former nomads were only recently able to gain a foothold in the world of Veloren. \n\
|
||||||
\n\
|
\n\
|
||||||
Their greatest strengths are their adaptability and intelligence, which makes them allrounders in many fields. \n\
|
Their greatest strengths are their adaptability and intelligence, which makes them allrounders in many fields.";
|
||||||
\n\
|
|
||||||
Some become wicked witches, slimy scoundrels, and members of the underworld, while others become witch-hunters, sages, and noble knights. \n\
|
|
||||||
\n\
|
|
||||||
This diversity however creates constant conflict and antagonism between humans themselves.";
|
|
||||||
const ORC_DESC: &str =
|
const ORC_DESC: &str =
|
||||||
"They are considered brutal, rude and combative. \n\
|
"They are considered brutal, rude and combative. \n\
|
||||||
But once you gained their trust they will be loyal friends \n\
|
But once you gained their trust they will be loyal friends \n\
|
||||||
that follow a strict code of honor in all of their actions. \n\
|
that follow a strict code of honor in all of their actions. \n\
|
||||||
\n\
|
\n\
|
||||||
Their warriors are masters of melee combat, but their true power \
|
Their warriors are masters of melee combat, but their true power \
|
||||||
comes from the magical rituals of their powerful shamans.";
|
comes from the magical rituals of their powerful shamans.";
|
||||||
const DWARF_DESC: &str =
|
const DWARF_DESC: &str =
|
||||||
"Smoking chimneys, the sound of countless hammers and hoes. \
|
"Smoking chimneys, the sound of countless hammers and hoes. \
|
||||||
Infinite tunnel systems to track down even the last chunk of metal in the ground. \n\
|
Infinite tunnel systems to track down even the last chunk of metal in the ground. \n\
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
window::{Event, Window},
|
window::{Event, Window},
|
||||||
GlobalState, PlayState, PlayStateResult,
|
GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
|
use client::{self, Client};
|
||||||
use common::clock::Clock;
|
use common::clock::Clock;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||||
@ -56,14 +57,42 @@ impl PlayState for MainMenuState {
|
|||||||
|
|
||||||
// Maintain the UI
|
// Maintain the UI
|
||||||
for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) {
|
for event in self.main_menu_ui.maintain(global_state.window.renderer_mut()) {
|
||||||
match event {
|
match event {
|
||||||
MainMenuEvent::LoginAttempt{ username, server_address } =>
|
MainMenuEvent::LoginAttempt{ username, server_address } => {
|
||||||
// For now just start a new session
|
use std::net::ToSocketAddrs;
|
||||||
return PlayStateResult::Push(
|
const DEFAULT_PORT: u16 = 59003;
|
||||||
Box::new(CharSelectionState::new(&mut global_state.window))
|
// Parses ip address or resolves hostname
|
||||||
),
|
// Note: if you use an ipv6 address the number after the last colon will be used as the port unless you use [] around the address
|
||||||
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
|
match server_address.to_socket_addrs().or((server_address.as_str(), DEFAULT_PORT).to_socket_addrs()) {
|
||||||
}
|
Ok(mut socket_adders) => {
|
||||||
|
while let Some(socket_addr) = socket_adders.next() {
|
||||||
|
// TODO: handle error
|
||||||
|
match Client::new(socket_addr) {
|
||||||
|
Ok(client) => {
|
||||||
|
return PlayStateResult::Push(
|
||||||
|
Box::new(CharSelectionState::new(
|
||||||
|
&mut global_state.window,
|
||||||
|
std::rc::Rc::new(std::cell::RefCell::new(client.with_test_state())) // <--- TODO: Remove this
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(client::Error::Network(_)) => {} // assume connection failed and try next address
|
||||||
|
Err(err) => {
|
||||||
|
panic!("Unexpected non Network error when creating client: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parsing/host name resolution successful but no connection succeeded
|
||||||
|
self.main_menu_ui.login_error("Could not connect to address".to_string());
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
// Error parsing input string or error resolving host name
|
||||||
|
self.main_menu_ui.login_error("No such host is known".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the UI to the screen
|
// Draw the UI to the screen
|
||||||
|
@ -6,8 +6,9 @@ use crate::{
|
|||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color::TRANSPARENT,
|
color::TRANSPARENT,
|
||||||
image::Id as ImgId,
|
image::Id as ImgId,
|
||||||
|
position::Dimension,
|
||||||
text::font::Id as FontId,
|
text::font::Id as FontId,
|
||||||
widget::{text_box::Event as TextBoxEvent, Button, Image, TextBox},
|
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox},
|
||||||
widget_ids, Borderable, Color,
|
widget_ids, Borderable, Color,
|
||||||
Colorable, Labelable, Positionable, Sizeable, Widget,
|
Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||||
};
|
};
|
||||||
@ -21,6 +22,8 @@ widget_ids! {
|
|||||||
// Login, Singleplayer
|
// Login, Singleplayer
|
||||||
login_button,
|
login_button,
|
||||||
login_text,
|
login_text,
|
||||||
|
login_error,
|
||||||
|
login_error_bg,
|
||||||
address_text,
|
address_text,
|
||||||
address_bg,
|
address_bg,
|
||||||
address_field,
|
address_field,
|
||||||
@ -101,6 +104,7 @@ pub struct MainMenuUi {
|
|||||||
font_opensans: FontId,
|
font_opensans: FontId,
|
||||||
username: String,
|
username: String,
|
||||||
server_address: String,
|
server_address: String,
|
||||||
|
login_error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainMenuUi {
|
impl MainMenuUi {
|
||||||
@ -134,7 +138,8 @@ impl MainMenuUi {
|
|||||||
font_metamorph,
|
font_metamorph,
|
||||||
font_opensans,
|
font_opensans,
|
||||||
username: "Username".to_string(),
|
username: "Username".to_string(),
|
||||||
server_address: "Server Address".to_string(),
|
server_address: "veloren.mac94.de".to_string(),
|
||||||
|
login_error: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +154,7 @@ impl MainMenuUi {
|
|||||||
.w_h(346.0, 111.0)
|
.w_h(346.0, 111.0)
|
||||||
.top_left_with_margins(30.0, 40.0)
|
.top_left_with_margins(30.0, 40.0)
|
||||||
.label("Alpha 0.1")
|
.label("Alpha 0.1")
|
||||||
.label_rgba(255.0, 255.0, 255.0, 1.0)
|
.label_rgba(1.0, 1.0, 1.0, 1.0)
|
||||||
.label_font_size(10)
|
.label_font_size(10)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(-40.0))
|
.label_y(conrod_core::position::Relative::Scalar(-40.0))
|
||||||
.label_x(conrod_core::position::Relative::Scalar(-100.0))
|
.label_x(conrod_core::position::Relative::Scalar(-100.0))
|
||||||
@ -159,6 +164,7 @@ impl MainMenuUi {
|
|||||||
// Used when the login button is pressed, or enter is pressed within input field
|
// Used when the login button is pressed, or enter is pressed within input field
|
||||||
macro_rules! login {
|
macro_rules! login {
|
||||||
() => {
|
() => {
|
||||||
|
self.login_error = None;
|
||||||
events.push(Event::LoginAttempt {
|
events.push(Event::LoginAttempt {
|
||||||
username: self.username.clone(),
|
username: self.username.clone(),
|
||||||
server_address: self.server_address.clone(),
|
server_address: self.server_address.clone(),
|
||||||
@ -176,7 +182,7 @@ impl MainMenuUi {
|
|||||||
.mid_bottom_with_margin_on(self.ids.username_bg, 44.0 / 2.0)
|
.mid_bottom_with_margin_on(self.ids.username_bg, 44.0 / 2.0)
|
||||||
.font_size(20)
|
.font_size(20)
|
||||||
.font_id(self.font_opensans)
|
.font_id(self.font_opensans)
|
||||||
.text_color(Color::Rgba(220.0, 220.0, 220.0, 0.8))
|
.text_color(Color::Rgba(0.86, 0.86, 0.86, 0.8))
|
||||||
// transparent background
|
// transparent background
|
||||||
.color(TRANSPARENT)
|
.color(TRANSPARENT)
|
||||||
.border_color(TRANSPARENT)
|
.border_color(TRANSPARENT)
|
||||||
@ -187,9 +193,28 @@ impl MainMenuUi {
|
|||||||
// Note: TextBox limits the input string length to what fits in it
|
// Note: TextBox limits the input string length to what fits in it
|
||||||
self.username = username.to_string();
|
self.username = username.to_string();
|
||||||
}
|
}
|
||||||
TextBoxEvent::Enter => login!(),
|
TextBoxEvent::Enter => { login!(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Login error
|
||||||
|
if let Some(msg) = &self.login_error {
|
||||||
|
let text = Text::new(&msg)
|
||||||
|
.rgba(0.5, 0.0, 0.0, 1.0)
|
||||||
|
.font_size(30)
|
||||||
|
.font_id(self.font_opensans);
|
||||||
|
let x = match text.get_x_dimension(ui_widgets) {
|
||||||
|
Dimension::Absolute(x) => x + 10.0,
|
||||||
|
_ => 0.0,
|
||||||
|
};
|
||||||
|
Rectangle::fill([x, 40.0])
|
||||||
|
.rgba(0.2, 0.3, 0.3, 0.7)
|
||||||
|
.parent(ui_widgets.window)
|
||||||
|
.up_from(self.ids.username_bg, 35.0)
|
||||||
|
.set(self.ids.login_error_bg, ui_widgets);
|
||||||
|
text
|
||||||
|
.middle_of(self.ids.login_error_bg)
|
||||||
|
.set(self.ids.login_error, ui_widgets);
|
||||||
|
}
|
||||||
// Server address
|
// Server address
|
||||||
Image::new(self.imgs.input_bg)
|
Image::new(self.imgs.input_bg)
|
||||||
.w_h(337.0, 67.0)
|
.w_h(337.0, 67.0)
|
||||||
@ -200,7 +225,7 @@ impl MainMenuUi {
|
|||||||
.mid_bottom_with_margin_on(self.ids.address_bg, 44.0 / 2.0)
|
.mid_bottom_with_margin_on(self.ids.address_bg, 44.0 / 2.0)
|
||||||
.font_size(20)
|
.font_size(20)
|
||||||
.font_id(self.font_opensans)
|
.font_id(self.font_opensans)
|
||||||
.text_color(Color::Rgba(220.0, 220.0, 220.0, 0.8))
|
.text_color(Color::Rgba(0.86, 0.86, 0.86, 0.8))
|
||||||
// transparent background
|
// transparent background
|
||||||
.color(TRANSPARENT)
|
.color(TRANSPARENT)
|
||||||
.border_color(TRANSPARENT)
|
.border_color(TRANSPARENT)
|
||||||
@ -210,7 +235,7 @@ impl MainMenuUi {
|
|||||||
TextBoxEvent::Update(server_address) => {
|
TextBoxEvent::Update(server_address) => {
|
||||||
self.server_address = server_address.to_string();
|
self.server_address = server_address.to_string();
|
||||||
}
|
}
|
||||||
TextBoxEvent::Enter => login!(),
|
TextBoxEvent::Enter => { login!(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Login button
|
// Login button
|
||||||
@ -221,7 +246,7 @@ impl MainMenuUi {
|
|||||||
.down_from(self.ids.address_bg, 20.0)
|
.down_from(self.ids.address_bg, 20.0)
|
||||||
.align_middle_x_of(self.ids.address_bg)
|
.align_middle_x_of(self.ids.address_bg)
|
||||||
.label("Login")
|
.label("Login")
|
||||||
.label_rgba(220.0, 220.0, 220.0, 0.8)
|
.label_rgba(0.86, 0.86, 0.86, 0.8)
|
||||||
.label_font_size(28)
|
.label_font_size(28)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(5.0))
|
.label_y(conrod_core::position::Relative::Scalar(5.0))
|
||||||
.set(self.ids.login_button, ui_widgets)
|
.set(self.ids.login_button, ui_widgets)
|
||||||
@ -229,7 +254,7 @@ impl MainMenuUi {
|
|||||||
{
|
{
|
||||||
login!();
|
login!();
|
||||||
}
|
}
|
||||||
//Singleplayer button
|
// Singleplayer button
|
||||||
if Button::image(self.imgs.login_button)
|
if Button::image(self.imgs.login_button)
|
||||||
.hover_image(self.imgs.login_button_hover)
|
.hover_image(self.imgs.login_button_hover)
|
||||||
.press_image(self.imgs.login_button_press)
|
.press_image(self.imgs.login_button_press)
|
||||||
@ -237,7 +262,7 @@ impl MainMenuUi {
|
|||||||
.down_from(self.ids.login_button, 20.0)
|
.down_from(self.ids.login_button, 20.0)
|
||||||
.align_middle_x_of(self.ids.address_bg)
|
.align_middle_x_of(self.ids.address_bg)
|
||||||
.label("Singleplayer")
|
.label("Singleplayer")
|
||||||
.label_rgba(220.0, 220.0, 220.0, 0.8)
|
.label_rgba(0.86, 0.86, 0.86, 0.8)
|
||||||
.label_font_size(26)
|
.label_font_size(26)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(5.0))
|
.label_y(conrod_core::position::Relative::Scalar(5.0))
|
||||||
.label_x(conrod_core::position::Relative::Scalar(2.0))
|
.label_x(conrod_core::position::Relative::Scalar(2.0))
|
||||||
@ -253,7 +278,7 @@ impl MainMenuUi {
|
|||||||
.hover_image(self.imgs.button_hover)
|
.hover_image(self.imgs.button_hover)
|
||||||
.press_image(self.imgs.button_press)
|
.press_image(self.imgs.button_press)
|
||||||
.label("Quit")
|
.label("Quit")
|
||||||
.label_rgba(220.0, 220.0, 220.0, 0.8)
|
.label_rgba(0.86, 0.86, 0.86, 0.8)
|
||||||
.label_font_size(20)
|
.label_font_size(20)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(3.0))
|
.label_y(conrod_core::position::Relative::Scalar(3.0))
|
||||||
.set(self.ids.quit_button, ui_widgets)
|
.set(self.ids.quit_button, ui_widgets)
|
||||||
@ -268,7 +293,7 @@ impl MainMenuUi {
|
|||||||
.hover_image(self.imgs.button_hover)
|
.hover_image(self.imgs.button_hover)
|
||||||
.press_image(self.imgs.button_press)
|
.press_image(self.imgs.button_press)
|
||||||
.label("Settings")
|
.label("Settings")
|
||||||
.label_rgba(220.0, 220.0, 220.0, 0.8)
|
.label_rgba(0.86, 0.86, 0.86, 0.8)
|
||||||
.label_font_size(20)
|
.label_font_size(20)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(3.0))
|
.label_y(conrod_core::position::Relative::Scalar(3.0))
|
||||||
.set(self.ids.settings_button, ui_widgets)
|
.set(self.ids.settings_button, ui_widgets)
|
||||||
@ -281,7 +306,7 @@ impl MainMenuUi {
|
|||||||
.hover_image(self.imgs.button_hover)
|
.hover_image(self.imgs.button_hover)
|
||||||
.press_image(self.imgs.button_press)
|
.press_image(self.imgs.button_press)
|
||||||
.label("Servers")
|
.label("Servers")
|
||||||
.label_rgba(220.0, 220.0, 220.0, 0.8)
|
.label_rgba(0.86, 0.86, 0.86, 0.8)
|
||||||
.label_font_size(20)
|
.label_font_size(20)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(3.0))
|
.label_y(conrod_core::position::Relative::Scalar(3.0))
|
||||||
.set(self.ids.servers_button, ui_widgets)
|
.set(self.ids.servers_button, ui_widgets)
|
||||||
@ -291,6 +316,10 @@ impl MainMenuUi {
|
|||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn login_error(&mut self, msg: String) {
|
||||||
|
self.login_error = Some(msg);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_event(&mut self, event: ui::Event) {
|
pub fn handle_event(&mut self, event: ui::Event) {
|
||||||
self.ui.handle_event(event);
|
self.ui.handle_event(event);
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ pub use self::{
|
|||||||
Locals as TerrainLocals,
|
Locals as TerrainLocals,
|
||||||
},
|
},
|
||||||
ui::{
|
ui::{
|
||||||
push_quad_to_mesh as push_ui_quad_to_mesh,
|
create_quad as create_ui_quad,
|
||||||
push_tri_to_mesh as push_ui_tri_to_mesh,
|
create_tri as create_ui_tri,
|
||||||
Mode as UiMode,
|
Mode as UiMode,
|
||||||
UiPipeline,
|
UiPipeline,
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,6 @@ use super::super::{
|
|||||||
Pipeline,
|
Pipeline,
|
||||||
TgtColorFmt,
|
TgtColorFmt,
|
||||||
TgtDepthFmt,
|
TgtDepthFmt,
|
||||||
Mesh,
|
|
||||||
Quad,
|
Quad,
|
||||||
Tri,
|
Tri,
|
||||||
};
|
};
|
||||||
@ -66,7 +65,7 @@ impl Mode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_quad_to_mesh(mesh: &mut Mesh<UiPipeline>, rect: Aabr<f32>, uv_rect: Aabr<f32>, color: [f32; 4], mode: Mode) {
|
pub fn create_quad(rect: Aabr<f32>, uv_rect: Aabr<f32>, color: [f32; 4], mode: Mode) -> Quad<UiPipeline> {
|
||||||
let mode_val = mode.value();
|
let mode_val = mode.value();
|
||||||
let v = |pos, uv| {
|
let v = |pos, uv| {
|
||||||
Vertex {
|
Vertex {
|
||||||
@ -83,15 +82,15 @@ pub fn push_quad_to_mesh(mesh: &mut Mesh<UiPipeline>, rect: Aabr<f32>, uv_rect:
|
|||||||
|
|
||||||
let (l, b, r, t) = aabr_to_lbrt(rect);
|
let (l, b, r, t) = aabr_to_lbrt(rect);
|
||||||
let (uv_l, uv_b, uv_r, uv_t) = aabr_to_lbrt(uv_rect);
|
let (uv_l, uv_b, uv_r, uv_t) = aabr_to_lbrt(uv_rect);
|
||||||
mesh.push_quad(Quad::new(
|
Quad::new(
|
||||||
v([r, t], [uv_r, uv_t]),
|
v([r, t], [uv_r, uv_t]),
|
||||||
v([l, t], [uv_l, uv_t]),
|
v([l, t], [uv_l, uv_t]),
|
||||||
v([l, b], [uv_l, uv_b]),
|
v([l, b], [uv_l, uv_b]),
|
||||||
v([r, b], [uv_r, uv_b]),
|
v([r, b], [uv_r, uv_b]),
|
||||||
));
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_tri_to_mesh(mesh: &mut Mesh<UiPipeline>, tri: [[f32; 2]; 3], uv_tri: [[f32; 2]; 3], color: [f32; 4], mode: Mode) {
|
pub fn create_tri(tri: [[f32; 2]; 3], uv_tri: [[f32; 2]; 3], color: [f32; 4], mode: Mode) -> Tri<UiPipeline> {
|
||||||
let mode_val = mode.value();
|
let mode_val = mode.value();
|
||||||
let v = |pos, uv| {
|
let v = |pos, uv| {
|
||||||
Vertex {
|
Vertex {
|
||||||
@ -101,9 +100,9 @@ pub fn push_tri_to_mesh(mesh: &mut Mesh<UiPipeline>, tri: [[f32; 2]; 3], uv_tri:
|
|||||||
mode: mode_val,
|
mode: mode_val,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mesh.push_tri(Tri::new(
|
Tri::new(
|
||||||
v([tri[0][0], tri[0][1]], [uv_tri[0][0], uv_tri[0][1]]),
|
v([tri[0][0], tri[0][1]], [uv_tri[0][0], uv_tri[0][1]]),
|
||||||
v([tri[1][0], tri[1][1]], [uv_tri[1][0], uv_tri[1][1]]),
|
v([tri[1][0], tri[1][1]], [uv_tri[1][0], uv_tri[1][1]]),
|
||||||
v([tri[2][0], tri[2][1]], [uv_tri[2][0], uv_tri[2][1]]),
|
v([tri[2][0], tri[2][1]], [uv_tri[2][0], uv_tri[2][1]]),
|
||||||
));
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,48 @@
|
|||||||
// Standard
|
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
// Library
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// Project
|
|
||||||
use client::{self, Client};
|
|
||||||
use common::clock::Clock;
|
use common::clock::Clock;
|
||||||
|
use client::{
|
||||||
// Convert Input to a valid string
|
self,
|
||||||
use std::net::ToSocketAddrs;
|
Client,
|
||||||
|
};
|
||||||
// Crate
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hud::{Event as HudEvent, Hud},
|
Error,
|
||||||
|
PlayState,
|
||||||
|
PlayStateResult,
|
||||||
|
GlobalState,
|
||||||
key_state::KeyState,
|
key_state::KeyState,
|
||||||
|
window::{Event, Key, Window},
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
scene::Scene,
|
scene::Scene,
|
||||||
window::{Event, Key, Window},
|
hud::{Hud, Event as HudEvent},
|
||||||
Error, GlobalState, PlayState, PlayStateResult,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FPS: u64 = 60;
|
const FPS: u64 = 60;
|
||||||
|
|
||||||
pub struct SessionState {
|
pub struct SessionState {
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
client: Client,
|
client: Rc<RefCell<Client>>,
|
||||||
key_state: KeyState,
|
key_state: KeyState,
|
||||||
hud: Hud,
|
hud: Hud,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Represents an active game session (i.e: one that is being played)
|
/// Represents an active game session (i.e: one that is being played)
|
||||||
impl SessionState {
|
impl SessionState {
|
||||||
/// Create a new `SessionState`
|
/// Create a new `SessionState`
|
||||||
pub fn new(window: &mut Window) -> Result<Self, Error> {
|
pub fn new(window: &mut Window, client: Rc<RefCell<Client>>) -> Self {
|
||||||
let client = Client::new(([127, 0, 0, 1], 59003))?.with_test_state(); // <--- TODO: Remove this
|
// Create a scene for this session. The scene handles visible elements of the game world
|
||||||
Ok(Self {
|
let scene = Scene::new(window.renderer_mut(), &client.borrow());
|
||||||
// Create a scene for this session. The scene handles visible elements of the game world
|
Self {
|
||||||
scene: Scene::new(window.renderer_mut(), &client),
|
scene,
|
||||||
client,
|
client,
|
||||||
key_state: KeyState::new(),
|
key_state: KeyState::new(),
|
||||||
hud: Hud::new(window),
|
hud: Hud::new(window),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// The background colour
|
// The background colour
|
||||||
const BG_COLOR: Rgba<f32> = Rgba {
|
const BG_COLOR: Rgba<f32> = Rgba {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
@ -65,7 +63,7 @@ impl SessionState {
|
|||||||
let dir_vec = self.key_state.dir_vec();
|
let dir_vec = self.key_state.dir_vec();
|
||||||
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
||||||
|
|
||||||
for event in self.client.tick(client::Input { move_dir }, dt)? {
|
for event in self.client.borrow_mut().tick(client::Input { move_dir }, dt)? {
|
||||||
match event {
|
match event {
|
||||||
client::Event::Chat(msg) => {
|
client::Event::Chat(msg) => {
|
||||||
self.hud.new_message(msg);
|
self.hud.new_message(msg);
|
||||||
@ -78,7 +76,7 @@ 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) {
|
pub fn cleanup(&mut self) {
|
||||||
self.client.cleanup();
|
self.client.borrow_mut().cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the session to the screen.
|
/// Render the session to the screen.
|
||||||
@ -110,7 +108,7 @@ impl PlayState for SessionState {
|
|||||||
for x in -6..7 {
|
for x in -6..7 {
|
||||||
for y in -6..7 {
|
for y in -6..7 {
|
||||||
for z in -1..2 {
|
for z in -1..2 {
|
||||||
self.client.load_chunk(Vec3::new(x, y, z));
|
self.client.borrow_mut().load_chunk(Vec3::new(x, y, z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,6 +117,7 @@ impl PlayState for SessionState {
|
|||||||
loop {
|
loop {
|
||||||
// Handle window events
|
// Handle window events
|
||||||
for event in global_state.window.fetch_events() {
|
for event in global_state.window.fetch_events() {
|
||||||
|
|
||||||
// Pass all events to the ui first
|
// Pass all events to the ui first
|
||||||
if self.hud.handle_event(event.clone()) {
|
if self.hud.handle_event(event.clone()) {
|
||||||
continue;
|
continue;
|
||||||
@ -167,16 +166,15 @@ impl PlayState for SessionState {
|
|||||||
self.tick(clock.get_last_delta())
|
self.tick(clock.get_last_delta())
|
||||||
.expect("Failed to tick the scene");
|
.expect("Failed to tick the scene");
|
||||||
|
|
||||||
// Maintain the scene
|
// Maintain the scene
|
||||||
self.scene
|
self.scene.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
||||||
.maintain(global_state.window.renderer_mut(), &self.client);
|
|
||||||
// Maintain the UI
|
// Maintain the UI
|
||||||
for event in self.hud.maintain(global_state.window.renderer_mut()) {
|
for event in self.hud.maintain(global_state.window.renderer_mut()) {
|
||||||
match event {
|
match event {
|
||||||
HudEvent::SendMessage(msg) => {
|
HudEvent::SendMessage(msg) => {
|
||||||
// TODO: Handle result
|
// TODO: Handle result
|
||||||
self.client.send_chat(msg);
|
self.client.borrow_mut().send_chat(msg);
|
||||||
}
|
},
|
||||||
HudEvent::Logout => return PlayStateResult::Pop,
|
HudEvent::Logout => return PlayStateResult::Pop,
|
||||||
HudEvent::Quit => return PlayStateResult::Shutdown,
|
HudEvent::Quit => return PlayStateResult::Shutdown,
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ use crate::{
|
|||||||
Texture,
|
Texture,
|
||||||
UiPipeline,
|
UiPipeline,
|
||||||
UiMode,
|
UiMode,
|
||||||
push_ui_quad_to_mesh,
|
create_ui_quad,
|
||||||
push_ui_tri_to_mesh,
|
create_ui_tri,
|
||||||
},
|
},
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
@ -412,13 +412,12 @@ impl Ui {
|
|||||||
min: Vec2::new(uv_l, uv_b),
|
min: Vec2::new(uv_l, uv_b),
|
||||||
max: Vec2::new(uv_r, uv_t),
|
max: Vec2::new(uv_r, uv_t),
|
||||||
};
|
};
|
||||||
push_ui_quad_to_mesh(
|
mesh.push_quad(create_ui_quad(
|
||||||
&mut mesh,
|
|
||||||
gl_aabr(rect),
|
gl_aabr(rect),
|
||||||
uv,
|
uv,
|
||||||
color,
|
color,
|
||||||
UiMode::Image,
|
UiMode::Image,
|
||||||
);
|
));
|
||||||
|
|
||||||
}
|
}
|
||||||
PrimitiveKind::Text { color, text, font_id } => {
|
PrimitiveKind::Text { color, text, font_id } => {
|
||||||
@ -463,13 +462,12 @@ impl Ui {
|
|||||||
(screen_rect.min.y as f32 / screen_h - 0.5) * -2.0,
|
(screen_rect.min.y as f32 / screen_h - 0.5) * -2.0,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
push_ui_quad_to_mesh(
|
mesh.push_quad(create_ui_quad(
|
||||||
&mut mesh,
|
|
||||||
rect,
|
rect,
|
||||||
uv,
|
uv,
|
||||||
color,
|
color,
|
||||||
UiMode::Text,
|
UiMode::Text,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,8 +481,7 @@ impl Ui {
|
|||||||
|
|
||||||
switch_to_plain_state!();
|
switch_to_plain_state!();
|
||||||
|
|
||||||
push_ui_quad_to_mesh(
|
mesh.push_quad(create_ui_quad(
|
||||||
&mut mesh,
|
|
||||||
gl_aabr(rect),
|
gl_aabr(rect),
|
||||||
Aabr {
|
Aabr {
|
||||||
min: Vec2::new(0.0, 0.0),
|
min: Vec2::new(0.0, 0.0),
|
||||||
@ -492,7 +489,7 @@ impl Ui {
|
|||||||
},
|
},
|
||||||
color,
|
color,
|
||||||
UiMode::Geometry,
|
UiMode::Geometry,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
PrimitiveKind::TrianglesSingleColor { color, triangles } => {
|
PrimitiveKind::TrianglesSingleColor { color, triangles } => {
|
||||||
// Don't draw transparent triangle or switch state if there are actually no triangles
|
// Don't draw transparent triangle or switch state if there are actually no triangles
|
||||||
@ -518,13 +515,12 @@ impl Ui {
|
|||||||
p1.into_array(),
|
p1.into_array(),
|
||||||
p3.into_array(),
|
p3.into_array(),
|
||||||
]};
|
]};
|
||||||
push_ui_tri_to_mesh(
|
mesh.push_tri(create_ui_tri(
|
||||||
&mut mesh,
|
|
||||||
triangle,
|
triangle,
|
||||||
[[0.0; 2]; 3],
|
[[0.0; 2]; 3],
|
||||||
color,
|
color,
|
||||||
UiMode::Geometry,
|
UiMode::Geometry,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user