2019-04-14 23:28:29 +00:00
|
|
|
use client::{error::Error as ClientError, Client};
|
|
|
|
use common::comp;
|
|
|
|
use std::{
|
|
|
|
sync::mpsc::{channel, Receiver, TryRecvError},
|
|
|
|
thread::{self, JoinHandle},
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
// Error parsing input string or error resolving host name
|
|
|
|
BadAddress(std::io::Error),
|
|
|
|
// Parsing yielded an empty iterator (specifically to_socket_addrs())
|
|
|
|
NoAddress,
|
|
|
|
// Parsing/host name resolution successful but could not connect
|
|
|
|
ConnectionFailed(ClientError),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Used to asynchronusly parse the server address, resolve host names, and create the client (which involves establishing a connection to the server)
|
|
|
|
pub struct ClientInit {
|
|
|
|
rx: Receiver<Result<Client, Error>>,
|
|
|
|
}
|
|
|
|
impl ClientInit {
|
2019-04-29 20:37:19 +00:00
|
|
|
pub fn new(connection_args: (String, u16, bool), client_args: (comp::Player, u64)) -> Self {
|
2019-04-14 23:28:29 +00:00
|
|
|
let (server_address, default_port, prefer_ipv6) = connection_args;
|
2019-04-19 07:35:23 +00:00
|
|
|
let (player, view_distance) = client_args;
|
2019-04-14 23:28:29 +00:00
|
|
|
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
|
|
|
let handle = Some(thread::spawn(move || {
|
|
|
|
use std::net::ToSocketAddrs;
|
|
|
|
// 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
|
|
|
|
match server_address
|
|
|
|
.to_socket_addrs()
|
|
|
|
.or((server_address.as_ref(), default_port).to_socket_addrs())
|
|
|
|
{
|
|
|
|
Ok(socket_adders) => {
|
|
|
|
let (first_addrs, second_addrs) =
|
|
|
|
socket_adders.partition::<Vec<_>, _>(|a| a.is_ipv6() == prefer_ipv6);
|
|
|
|
|
|
|
|
let mut last_err = None;
|
|
|
|
|
|
|
|
for socket_addr in first_addrs.into_iter().chain(second_addrs) {
|
2019-04-21 18:12:29 +00:00
|
|
|
match Client::new(socket_addr, view_distance) {
|
|
|
|
Ok(mut client) => {
|
|
|
|
client.register(player);
|
2019-04-15 00:23:26 +00:00
|
|
|
let _ = tx.send(Ok(client));
|
2019-04-14 23:28:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
match err {
|
|
|
|
// assume connection failed and try next address
|
|
|
|
ClientError::Network(_) => {
|
|
|
|
last_err = Some(Error::ConnectionFailed(err))
|
|
|
|
}
|
|
|
|
// TODO: handle error?
|
|
|
|
_ => panic!(
|
|
|
|
"Unexpected non-network error when creating client: {:?}",
|
|
|
|
err
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Parsing/host name resolution successful but no connection succeeded
|
2019-04-15 00:23:26 +00:00
|
|
|
let _ = tx.send(Err(last_err.unwrap_or(Error::NoAddress)));
|
2019-04-14 23:28:29 +00:00
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
// Error parsing input string or error resolving host name
|
2019-04-15 00:23:26 +00:00
|
|
|
let _ = tx.send(Err(Error::BadAddress(err)));
|
2019-04-14 23:28:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
ClientInit { rx }
|
|
|
|
}
|
|
|
|
// Returns None is the thread is still running
|
|
|
|
// Otherwise returns the Result of client creation
|
|
|
|
pub fn poll(&self) -> Option<Result<Client, Error>> {
|
|
|
|
match self.rx.try_recv() {
|
|
|
|
Ok(result) => Some(result),
|
|
|
|
Err(TryRecvError::Empty) => None,
|
|
|
|
Err(TryRecvError::Disconnected) => panic!("Thread panicked or already finished"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|