mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
make to_server() blocking, post fixes & cleanup, use server address
Former-commit-id: 7debc790e158a996ed58271d3307214e42b850bd
This commit is contained in:
parent
c10564d436
commit
fb83c836a1
4
.gitignore
vendored
4
.gitignore
vendored
@ -20,7 +20,5 @@
|
|||||||
**/server_conf.toml
|
**/server_conf.toml
|
||||||
**/keybinds.toml
|
**/keybinds.toml
|
||||||
assets/voxygen
|
assets/voxygen
|
||||||
UI3.rar
|
|
||||||
assets.rar
|
|
||||||
*.rar
|
*.rar
|
||||||
assets/voxygen
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e3083ec8e8e634af8c9daed00ea82435da195979
|
Subproject commit 6b64a65356c1fb4c77ad0295908f4006f75a5448
|
@ -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,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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,11 +7,11 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::assets;
|
use common::assets;
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color, Color,
|
color,
|
||||||
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},
|
||||||
widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget,
|
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
@ -246,13 +246,13 @@ pub(self) struct Imgs {
|
|||||||
impl Imgs {
|
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 fullpath: String = [
|
let fullpath: String = ["/voxygen/", filename].concat();
|
||||||
"/voxygen/",
|
|
||||||
filename,
|
|
||||||
].concat();
|
|
||||||
let image = image::load_from_memory(
|
let image = image::load_from_memory(
|
||||||
assets::load(fullpath.as_str()).expect("Error loading file").as_slice()
|
assets::load(fullpath.as_str())
|
||||||
).unwrap();
|
.expect("Error loading file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
ui.new_image(renderer, &image).unwrap()
|
ui.new_image(renderer, &image).unwrap()
|
||||||
};
|
};
|
||||||
Imgs {
|
Imgs {
|
||||||
@ -422,7 +422,7 @@ pub struct Hud {
|
|||||||
|
|
||||||
//#[inline]
|
//#[inline]
|
||||||
//pub fn rgba_bytes(r: u8, g: u8, b: u8, a: f32) -> Color {
|
//pub fn rgba_bytes(r: u8, g: u8, b: u8, a: f32) -> Color {
|
||||||
//Color::Rgba(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a)
|
//Color::Rgba(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
impl Hud {
|
impl Hud {
|
||||||
@ -479,12 +479,11 @@ impl Hud {
|
|||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
let ref mut ui_widgets = self.ui.set_widgets();
|
let ref mut ui_widgets = self.ui.set_widgets();
|
||||||
|
|
||||||
const TEXT_COLOR: Color = Color::Rgba(0.86, 0.86, 0.86, 0.8);
|
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||||
const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0);
|
const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0);
|
||||||
const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0);
|
const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0);
|
||||||
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
||||||
|
|
||||||
|
|
||||||
if self.show_ui {
|
if self.show_ui {
|
||||||
// Add Bag-Space Button
|
// Add Bag-Space Button
|
||||||
if self.inventorytest_button {
|
if self.inventorytest_button {
|
||||||
@ -753,7 +752,6 @@ impl Hud {
|
|||||||
.top_right_with_margins_on(self.ids.health_bar, 5.0, 0.0)
|
.top_right_with_margins_on(self.ids.health_bar, 5.0, 0.0)
|
||||||
.set(self.ids.health_bar_color, ui_widgets);
|
.set(self.ids.health_bar_color, ui_widgets);
|
||||||
|
|
||||||
|
|
||||||
// Mana Bar
|
// Mana Bar
|
||||||
Image::new(self.imgs.mana_bar)
|
Image::new(self.imgs.mana_bar)
|
||||||
.w_h(1120.0 / 6.0, 96.0 / 6.0)
|
.w_h(1120.0 / 6.0, 96.0 / 6.0)
|
||||||
@ -766,7 +764,6 @@ impl Hud {
|
|||||||
.top_left_with_margins_on(self.ids.mana_bar, 5.0, 0.0)
|
.top_left_with_margins_on(self.ids.mana_bar, 5.0, 0.0)
|
||||||
.set(self.ids.mana_bar_color, ui_widgets);
|
.set(self.ids.mana_bar_color, ui_widgets);
|
||||||
|
|
||||||
|
|
||||||
// Buffs/Debuffs
|
// Buffs/Debuffs
|
||||||
|
|
||||||
// Buffs
|
// Buffs
|
||||||
@ -1561,39 +1558,39 @@ impl Hud {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
WinEvent::KeyDown(key) if !self.typing => match key {
|
WinEvent::KeyDown(key) if !self.typing => match key {
|
||||||
Key::Map => {
|
Key::Map => {
|
||||||
self.toggle_map();
|
self.toggle_map();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Bag => {
|
Key::Bag => {
|
||||||
self.toggle_bag();
|
self.toggle_bag();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::QuestLog => {
|
Key::QuestLog => {
|
||||||
self.toggle_questlog();
|
self.toggle_questlog();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::CharacterWindow => {
|
Key::CharacterWindow => {
|
||||||
self.toggle_charwindow();
|
self.toggle_charwindow();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Social => {
|
Key::Social => {
|
||||||
self.toggle_social();
|
self.toggle_social();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Spellbook => {
|
Key::Spellbook => {
|
||||||
self.toggle_spellbook();
|
self.toggle_spellbook();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Settings => {
|
Key::Settings => {
|
||||||
self.toggle_settings();
|
self.toggle_settings();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Help => {
|
Key::Help => {
|
||||||
self.toggle_help();
|
self.toggle_help();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Interface => {
|
Key::Interface => {
|
||||||
self.toggle_ui();
|
self.toggle_ui();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -225,13 +225,13 @@ struct Imgs {
|
|||||||
impl Imgs {
|
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 fullpath: String = [
|
let fullpath: String = ["/voxygen/", filename].concat();
|
||||||
"/voxygen/",
|
|
||||||
filename,
|
|
||||||
].concat();
|
|
||||||
let image = image::load_from_memory(
|
let image = image::load_from_memory(
|
||||||
assets::load(fullpath.as_str()).expect("Error loading file").as_slice()
|
assets::load(fullpath.as_str())
|
||||||
).unwrap();
|
.expect("Error loading file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
ui.new_image(renderer, &image).unwrap()
|
ui.new_image(renderer, &image).unwrap()
|
||||||
};
|
};
|
||||||
Imgs {
|
Imgs {
|
||||||
@ -345,7 +345,7 @@ pub enum Event {
|
|||||||
Play,
|
Play,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEXT_COLOR: Color = Color::Rgba(0.86, 0.86, 0.86, 0.8);
|
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||||
|
|
||||||
pub struct CharSelectionUi {
|
pub struct CharSelectionUi {
|
||||||
ui: Ui,
|
ui: Ui,
|
||||||
@ -463,7 +463,7 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.test_char_l_button, ui_widgets)
|
.set(self.ids.test_char_l_button, ui_widgets)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
self.selected_char_no = Some(1);
|
self.selected_char_no = Some(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Veloren Logo and Alpha Version
|
// Veloren Logo and Alpha Version
|
||||||
@ -477,7 +477,7 @@ impl CharSelectionUi {
|
|||||||
.label_x(conrod_core::position::Relative::Scalar(-100.0))
|
.label_x(conrod_core::position::Relative::Scalar(-100.0))
|
||||||
.set(self.ids.v_logo, ui_widgets);
|
.set(self.ids.v_logo, ui_widgets);
|
||||||
|
|
||||||
if let Some(no) = self.selected_char_no {
|
if let Some(no) = self.selected_char_no {
|
||||||
// Selection_Window
|
// Selection_Window
|
||||||
Image::new(self.imgs.selection_window)
|
Image::new(self.imgs.selection_window)
|
||||||
.w_h(522.0, 722.0)
|
.w_h(522.0, 722.0)
|
||||||
@ -495,7 +495,6 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.char_level, ui_widgets);
|
.set(self.ids.char_level, ui_widgets);
|
||||||
|
|
||||||
|
|
||||||
// Selected Character
|
// Selected Character
|
||||||
if no == 1 {
|
if no == 1 {
|
||||||
Image::new(self.imgs.test_char_l_big)
|
Image::new(self.imgs.test_char_l_big)
|
||||||
@ -872,11 +871,11 @@ impl CharSelectionUi {
|
|||||||
Their greatest strengths are their adaptability and intelligence, which makes them allrounders in many fields.";
|
Their greatest strengths are their adaptability and intelligence, which makes them allrounders in many fields.";
|
||||||
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\
|
||||||
|
@ -10,8 +10,7 @@ use conrod_core::{
|
|||||||
position::Dimension,
|
position::Dimension,
|
||||||
text::font::Id as FontId,
|
text::font::Id as FontId,
|
||||||
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, 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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
@ -58,13 +57,13 @@ impl Imgs {
|
|||||||
fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs {
|
fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs {
|
||||||
// TODO: update paths
|
// TODO: update paths
|
||||||
let mut load = |filename| {
|
let mut load = |filename| {
|
||||||
let fullpath: String = [
|
let fullpath: String = ["/voxygen/", filename].concat();
|
||||||
"/voxygen/",
|
|
||||||
filename,
|
|
||||||
].concat();
|
|
||||||
let image = image::load_from_memory(
|
let image = image::load_from_memory(
|
||||||
assets::load(fullpath.as_str()).expect("Error loading file").as_slice()
|
assets::load(fullpath.as_str())
|
||||||
).unwrap();
|
.expect("Error loading file")
|
||||||
|
.as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
ui.new_image(renderer, &image).unwrap()
|
ui.new_image(renderer, &image).unwrap()
|
||||||
};
|
};
|
||||||
Imgs {
|
Imgs {
|
||||||
@ -170,7 +169,7 @@ impl MainMenuUi {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const TEXT_COLOR: Color = Color::Rgba(0.94, 0.94, 0.94, 0.8);
|
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||||
// Username
|
// Username
|
||||||
// TODO: get a lower resolution and cleaner input_bg.png
|
// TODO: get a lower resolution and cleaner input_bg.png
|
||||||
Image::new(self.imgs.input_bg)
|
Image::new(self.imgs.input_bg)
|
||||||
@ -193,7 +192,9 @@ 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
|
// Login error
|
||||||
@ -211,8 +212,7 @@ impl MainMenuUi {
|
|||||||
.parent(ui_widgets.window)
|
.parent(ui_widgets.window)
|
||||||
.up_from(self.ids.username_bg, 35.0)
|
.up_from(self.ids.username_bg, 35.0)
|
||||||
.set(self.ids.login_error_bg, ui_widgets);
|
.set(self.ids.login_error_bg, ui_widgets);
|
||||||
text
|
text.middle_of(self.ids.login_error_bg)
|
||||||
.middle_of(self.ids.login_error_bg)
|
|
||||||
.set(self.ids.login_error, ui_widgets);
|
.set(self.ids.login_error, ui_widgets);
|
||||||
}
|
}
|
||||||
// Server address
|
// Server address
|
||||||
@ -235,7 +235,9 @@ 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
|
||||||
|
Loading…
Reference in New Issue
Block a user