mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'protheory8/change-gameserver-addr-to-be-an-array-of-tuples' into 'master'
Remove gameserver_address setting and add protocols_and_addresses setting See merge request veloren/veloren!2925
This commit is contained in:
commit
6ea205fcc0
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -2893,9 +2893,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.107"
|
||||
version = "0.2.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@ -5383,9 +5383,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.2"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516"
|
||||
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
@ -6374,6 +6374,7 @@ dependencies = [
|
||||
"rustls 0.20.1",
|
||||
"serde",
|
||||
"shellexpand",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
|
@ -20,6 +20,8 @@ network-protocol = { package = "veloren-network-protocol", path = "protocol" }
|
||||
#serialisation
|
||||
bincode = "1.3.2"
|
||||
serde = { version = "1.0" }
|
||||
#ipv4/ipv6 behavior
|
||||
socket2 = "0.4.4"
|
||||
#sending
|
||||
crossbeam-channel = "0.5"
|
||||
tokio = { version = "1.14", default-features = false, features = ["io-util", "macros", "rt", "net", "time"] }
|
||||
|
@ -94,7 +94,21 @@ impl Protocols {
|
||||
s2s_stop_listening_r: oneshot::Receiver<()>,
|
||||
c2s_protocol_s: mpsc::UnboundedSender<(Self, Cid)>,
|
||||
) -> std::io::Result<()> {
|
||||
let listener = net::TcpListener::bind(addr).await?;
|
||||
use socket2::{Domain, Socket, Type};
|
||||
let domain = Domain::for_address(addr);
|
||||
let socket2_socket = Socket::new(domain, Type::STREAM, None)?;
|
||||
if domain == Domain::IPV6 {
|
||||
socket2_socket.set_only_v6(true)?
|
||||
}
|
||||
socket2_socket.set_nonblocking(true)?; // Needed by Tokio
|
||||
// See https://docs.rs/tokio/latest/tokio/net/struct.TcpSocket.html
|
||||
#[cfg(not(windows))]
|
||||
socket2_socket.set_reuse_address(true)?;
|
||||
let socket2_addr = addr.into();
|
||||
socket2_socket.bind(&socket2_addr)?;
|
||||
socket2_socket.listen(1024)?;
|
||||
let std_listener: std::net::TcpListener = socket2_socket.into();
|
||||
let listener = tokio::net::TcpListener::from_std(std_listener)?;
|
||||
trace!(?addr, "Tcp Listener bound");
|
||||
let mut end_receiver = s2s_stop_listening_r.fuse();
|
||||
tokio::spawn(async move {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use lazy_static::*;
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
net::{Ipv4Addr, SocketAddr},
|
||||
sync::{
|
||||
atomic::{AtomicU16, AtomicU64, Ordering},
|
||||
Arc,
|
||||
@ -89,8 +89,8 @@ pub fn tcp() -> (ListenAddr, ConnectAddr) {
|
||||
}
|
||||
let port = PORTS.fetch_add(1, Ordering::Relaxed);
|
||||
(
|
||||
ListenAddr::Tcp(SocketAddr::from(([127, 0, 0, 1], port))),
|
||||
ConnectAddr::Tcp(SocketAddr::from(([127, 0, 0, 1], port))),
|
||||
ListenAddr::Tcp(SocketAddr::from((Ipv4Addr::LOCALHOST, port))),
|
||||
ConnectAddr::Tcp(SocketAddr::from((Ipv4Addr::LOCALHOST, port))),
|
||||
)
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ pub fn quic() -> (ListenAddr, ConnectAddr) {
|
||||
let server_config = quinn::ServerConfig::with_single_cert(vec![cert], key)
|
||||
.expect("Server Config Cert/Key failed");
|
||||
let client_config = quinn::ClientConfig::with_root_certificates(root_store);
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::net::IpAddr;
|
||||
(
|
||||
ListenAddr::Quic(
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port),
|
||||
|
@ -262,3 +262,48 @@ fn multiple_try_recv() {
|
||||
assert_eq!(s1_b.try_recv::<String>(), Err(StreamError::StreamClosed));
|
||||
drop((_n_a, _n_b, _p_a, _p_b)); //clean teardown
|
||||
}
|
||||
|
||||
/// If we listen on a IPv6 UNSPECIFIED address, on linux it will automatically
|
||||
/// listen on the respective IPv4 address. This must not be as we should behave
|
||||
/// similar under windows and linux.
|
||||
///
|
||||
/// As most CI servers don't have IPv6 configured, this would return
|
||||
/// ConnectFailed(Io(Os { code: 99, kind: AddrNotAvailable, message: "Cannot
|
||||
/// assign requested address" })) we have to disable this test in CI, but it was
|
||||
/// manually tested on linux and windows
|
||||
///
|
||||
/// On Windows this test must be executed as root to listen on IPv6::UNSPECIFIED
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn listen_on_ipv6_doesnt_block_ipv4() {
|
||||
let (_, _) = helper::setup(false, 0);
|
||||
let tcpv4 = tcp();
|
||||
let port = if let ListenAddr::Tcp(x) = tcpv4.0 {
|
||||
x.port()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let tcpv6 = (
|
||||
ListenAddr::Tcp(std::net::SocketAddr::from((
|
||||
std::net::Ipv6Addr::UNSPECIFIED,
|
||||
port,
|
||||
))),
|
||||
ConnectAddr::Tcp(std::net::SocketAddr::from((
|
||||
std::net::Ipv6Addr::UNSPECIFIED,
|
||||
port,
|
||||
))),
|
||||
);
|
||||
|
||||
let (_r, _n_a, _p_a, mut s1_a, _n_b, _p_b, mut s1_b) = network_participant_stream(tcpv6);
|
||||
std::thread::sleep(SLEEP_EXTERNAL);
|
||||
let (_r2, _n_a2, _p_a2, mut s1_a2, _n_b2, _p_b2, mut s1_b2) = network_participant_stream(tcpv4);
|
||||
|
||||
s1_a.send(42u32).unwrap();
|
||||
s1_a2.send(1337u32).unwrap();
|
||||
std::thread::sleep(SLEEP_EXTERNAL);
|
||||
assert_eq!(s1_b.try_recv::<u32>(), Ok(Some(42u32)));
|
||||
assert_eq!(s1_b2.try_recv::<u32>(), Ok(Some(1337u32)));
|
||||
|
||||
drop((s1_a, s1_b, _n_a, _n_b, _p_a, _p_b));
|
||||
drop((s1_a2, s1_b2, _n_a2, _n_b2, _p_a2, _p_b2)); //clean teardown
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use crate::{
|
||||
use common::{clock::Clock, consts::MIN_RECOMMENDED_TOKIO_THREADS};
|
||||
use common_base::span;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use server::{persistence::DatabaseSettings, Event, Input, Server};
|
||||
use server::{persistence::DatabaseSettings, settings::Protocol, Event, Input, Server};
|
||||
use std::{
|
||||
io,
|
||||
sync::{atomic::AtomicBool, mpsc, Arc},
|
||||
@ -154,7 +154,11 @@ fn main() -> io::Result<()> {
|
||||
|
||||
info!("Starting server...");
|
||||
|
||||
let server_port = &server_settings.gameserver_address.port();
|
||||
if no_auth {
|
||||
server_settings.auth_server_address = None;
|
||||
}
|
||||
|
||||
let protocols_and_addresses = server_settings.gameserver_protocols.clone();
|
||||
let metrics_port = &server_settings.metrics_address.port();
|
||||
// Create server
|
||||
let mut server = Server::new(
|
||||
@ -166,9 +170,21 @@ fn main() -> io::Result<()> {
|
||||
)
|
||||
.expect("Failed to create server instance!");
|
||||
|
||||
let mut gameserver_addresses = vec![];
|
||||
for protocol in protocols_and_addresses {
|
||||
gameserver_addresses.push(match protocol {
|
||||
Protocol::Tcp { address } => ("TCP", address),
|
||||
Protocol::Quic {
|
||||
address,
|
||||
cert_file_path: _,
|
||||
key_file_path: _,
|
||||
} => ("QUIC", address),
|
||||
})
|
||||
}
|
||||
|
||||
info!(
|
||||
?server_port,
|
||||
?metrics_port,
|
||||
?gameserver_addresses,
|
||||
"Server is ready to accept connections."
|
||||
);
|
||||
|
||||
|
@ -115,6 +115,8 @@ use crate::{
|
||||
use hashbrown::HashMap;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::settings::Protocol;
|
||||
|
||||
#[cfg(feature = "plugins")]
|
||||
use {
|
||||
common::uid::UidAllocator,
|
||||
@ -463,51 +465,75 @@ impl Server {
|
||||
)
|
||||
.await
|
||||
});
|
||||
runtime.block_on(network.listen(ListenAddr::Tcp(settings.gameserver_address)))?;
|
||||
runtime.block_on(network.listen(ListenAddr::Mpsc(14004)))?;
|
||||
if let Some(quic) = &settings.quic_files {
|
||||
use rustls_pemfile::Item;
|
||||
use std::fs;
|
||||
match || -> Result<_, Box<dyn std::error::Error>> {
|
||||
let key = fs::read(&quic.key)?;
|
||||
let key = if quic.key.extension().map_or(false, |x| x == "der") {
|
||||
rustls::PrivateKey(key)
|
||||
} else {
|
||||
debug!("convert pem key to der");
|
||||
let key = rustls_pemfile::read_all(&mut key.as_slice())?
|
||||
.into_iter()
|
||||
.find_map(|item| match item {
|
||||
Item::RSAKey(v) | Item::PKCS8Key(v) => Some(v),
|
||||
Item::X509Certificate(_) => None,
|
||||
})
|
||||
.ok_or("No valid pem key in file")?;
|
||||
rustls::PrivateKey(key)
|
||||
};
|
||||
let cert_chain = fs::read(&quic.cert)?;
|
||||
let cert_chain = if quic.cert.extension().map_or(false, |x| x == "der") {
|
||||
vec![rustls::Certificate(cert_chain)]
|
||||
} else {
|
||||
debug!("convert pem cert to der");
|
||||
let certs = rustls_pemfile::certs(&mut cert_chain.as_slice())?;
|
||||
certs.into_iter().map(rustls::Certificate).collect()
|
||||
};
|
||||
let server_config = quinn::ServerConfig::with_single_cert(cert_chain, key)?;
|
||||
Ok(server_config)
|
||||
}() {
|
||||
Ok(server_config) => {
|
||||
warn!(
|
||||
"QUIC is enabled. This is experimental and not recommended in production"
|
||||
);
|
||||
runtime.block_on(
|
||||
network
|
||||
.listen(ListenAddr::Quic(settings.gameserver_address, server_config)),
|
||||
)?;
|
||||
|
||||
let mut printed_quic_warning = false;
|
||||
for protocol in &settings.gameserver_protocols {
|
||||
match protocol {
|
||||
Protocol::Tcp { address } => {
|
||||
runtime.block_on(network.listen(ListenAddr::Tcp(*address)))?;
|
||||
},
|
||||
Err(e) => {
|
||||
error!(?e, ?settings.quic_files, "Failed to load Quic Certificate, run without Quic")
|
||||
Protocol::Quic {
|
||||
address,
|
||||
cert_file_path,
|
||||
key_file_path,
|
||||
} => {
|
||||
use rustls_pemfile::Item;
|
||||
use std::fs;
|
||||
|
||||
match || -> Result<_, Box<dyn std::error::Error>> {
|
||||
let key = fs::read(&key_file_path)?;
|
||||
let key = if key_file_path.extension().map_or(false, |x| x == "der") {
|
||||
rustls::PrivateKey(key)
|
||||
} else {
|
||||
debug!("convert pem key to der");
|
||||
let key = rustls_pemfile::read_all(&mut key.as_slice())?
|
||||
.into_iter()
|
||||
.find_map(|item| match item {
|
||||
Item::RSAKey(v) | Item::PKCS8Key(v) => Some(v),
|
||||
Item::X509Certificate(_) => None,
|
||||
})
|
||||
.ok_or("No valid pem key in file")?;
|
||||
rustls::PrivateKey(key)
|
||||
};
|
||||
let cert_chain = fs::read(&cert_file_path)?;
|
||||
let cert_chain = if cert_file_path.extension().map_or(false, |x| x == "der")
|
||||
{
|
||||
vec![rustls::Certificate(cert_chain)]
|
||||
} else {
|
||||
debug!("convert pem cert to der");
|
||||
let certs = rustls_pemfile::certs(&mut cert_chain.as_slice())?;
|
||||
certs.into_iter().map(rustls::Certificate).collect()
|
||||
};
|
||||
let server_config = quinn::ServerConfig::with_single_cert(cert_chain, key)?;
|
||||
Ok(server_config)
|
||||
}() {
|
||||
Ok(server_config) => {
|
||||
runtime.block_on(
|
||||
network.listen(ListenAddr::Quic(*address, server_config.clone())),
|
||||
)?;
|
||||
|
||||
if !printed_quic_warning {
|
||||
warn!(
|
||||
"QUIC is enabled. This is experimental and not recommended in \
|
||||
production"
|
||||
);
|
||||
printed_quic_warning = true;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!(
|
||||
?e,
|
||||
"Failed to load the TLS certificate, running without QUIC {}",
|
||||
*address
|
||||
);
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
runtime.block_on(network.listen(ListenAddr::Mpsc(14004)))?;
|
||||
|
||||
let connection_handler = ConnectionHandler::new(network, &runtime);
|
||||
|
||||
// Initiate real-time world simulation
|
||||
|
@ -23,7 +23,7 @@ use portpicker::pick_unused_port;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs,
|
||||
net::SocketAddr,
|
||||
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tracing::{error, warn};
|
||||
@ -37,12 +37,6 @@ const BANLIST_FILENAME: &str = "banlist.ron";
|
||||
const SERVER_DESCRIPTION_FILENAME: &str = "description.ron";
|
||||
const ADMINS_FILENAME: &str = "admins.ron";
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct X509FilePair {
|
||||
pub cert: PathBuf,
|
||||
pub key: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ServerBattleMode {
|
||||
Global(BattleMode),
|
||||
@ -65,6 +59,18 @@ impl ServerBattleMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Protocol {
|
||||
Quic {
|
||||
address: SocketAddr,
|
||||
cert_file_path: PathBuf,
|
||||
key_file_path: PathBuf,
|
||||
},
|
||||
Tcp {
|
||||
address: SocketAddr,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum CalendarMode {
|
||||
None,
|
||||
@ -91,10 +97,9 @@ impl CalendarMode {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Settings {
|
||||
pub gameserver_address: SocketAddr,
|
||||
pub gameserver_protocols: Vec<Protocol>,
|
||||
pub metrics_address: SocketAddr,
|
||||
pub auth_server_address: Option<String>,
|
||||
pub quic_files: Option<X509FilePair>,
|
||||
pub max_players: usize,
|
||||
pub world_seed: u32,
|
||||
pub battle_mode: ServerBattleMode,
|
||||
@ -121,10 +126,16 @@ pub struct Settings {
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
||||
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
||||
gameserver_protocols: vec![
|
||||
Protocol::Tcp {
|
||||
address: SocketAddr::from((Ipv6Addr::UNSPECIFIED, 14004)),
|
||||
},
|
||||
Protocol::Tcp {
|
||||
address: SocketAddr::from((Ipv4Addr::UNSPECIFIED, 14004)),
|
||||
},
|
||||
],
|
||||
metrics_address: SocketAddr::from((Ipv4Addr::LOCALHOST, 14005)),
|
||||
auth_server_address: Some("https://auth.veloren.net".into()),
|
||||
quic_files: None,
|
||||
world_seed: DEFAULT_WORLD_SEED,
|
||||
server_name: "Veloren Alpha".into(),
|
||||
max_players: 100,
|
||||
@ -195,18 +206,19 @@ impl Settings {
|
||||
pub fn singleplayer(path: &Path) -> Self {
|
||||
let load = Self::load(path);
|
||||
Self {
|
||||
//BUG: theoretically another process can grab the port between here and server
|
||||
// creation, however the timewindow is quite small
|
||||
gameserver_address: SocketAddr::from((
|
||||
[127, 0, 0, 1],
|
||||
pick_unused_port().expect("Failed to find unused port!"),
|
||||
)),
|
||||
// BUG: theoretically another process can grab the port between here and server
|
||||
// creation, however the time window is quite small.
|
||||
gameserver_protocols: vec![Protocol::Tcp {
|
||||
address: SocketAddr::from((
|
||||
Ipv4Addr::LOCALHOST,
|
||||
pick_unused_port().expect("Failed to find unused port!"),
|
||||
)),
|
||||
}],
|
||||
metrics_address: SocketAddr::from((
|
||||
[127, 0, 0, 1],
|
||||
Ipv4Addr::LOCALHOST,
|
||||
pick_unused_port().expect("Failed to find unused port!"),
|
||||
)),
|
||||
auth_server_address: None,
|
||||
quic_files: None,
|
||||
// If loading the default map file, make sure the seed is also default.
|
||||
world_seed: if load.map_file.is_some() {
|
||||
load.world_seed
|
||||
|
Loading…
Reference in New Issue
Block a user