Unify server pinging implementations between app and backend (#5510)
* Improve ping impl to bring parity to app lib impl * Fix issue with new impl * fix labrinth compile * wip: why do servers not provide server info.. * Fix ping impl overriding port * fix theseus_gui * remove unneeded recursion lmit
This commit is contained in:
@@ -6,14 +6,13 @@ use crate::state::attached_world_data::AttachedWorldData;
|
||||
use crate::state::{
|
||||
Profile, ProfileInstallStage, attached_world_data, server_join_log,
|
||||
};
|
||||
use crate::util::io;
|
||||
use crate::util::protocol_version::OLD_PROTOCOL_VERSIONS;
|
||||
pub use crate::util::protocol_version::ProtocolVersion;
|
||||
pub use crate::util::server_ping::{
|
||||
ServerGameProfile, ServerPlayers, ServerStatus, ServerVersion,
|
||||
};
|
||||
use crate::util::{io, server_ping};
|
||||
use crate::{Error, ErrorKind, Result, State, launcher};
|
||||
use async_minecraft_ping::ServerDescription;
|
||||
use crate::{Context, ErrorKind, Result, State, launcher};
|
||||
use async_walkdir::WalkDir;
|
||||
use async_zip::{Compression, ZipEntryBuilder};
|
||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||
@@ -910,67 +909,54 @@ pub async fn get_server_status(
|
||||
"Pinging {address} with protocol version {protocol_version:?}"
|
||||
);
|
||||
|
||||
get_server_status_old(address, protocol_version).await
|
||||
// get_server_status_new(address, protocol_version).await
|
||||
// get_server_status_old(address, protocol_version).await
|
||||
get_server_status_new(address, protocol_version).await
|
||||
}
|
||||
|
||||
async fn get_server_status_old(
|
||||
// async fn _get_server_status_old(
|
||||
// address: &str,
|
||||
// protocol_version: Option<ProtocolVersion>,
|
||||
// ) -> Result<ServerStatus> {
|
||||
// let (original_host, original_port) = parse_server_address(address)?;
|
||||
// let (host, port) =
|
||||
// resolve_server_address(original_host, original_port).await?;
|
||||
// tracing::debug!(
|
||||
// "Pinging {address} with protocol version {protocol_version:?}"
|
||||
// );
|
||||
// server_ping::get_server_status(
|
||||
// &(&host as &str, port),
|
||||
// (original_host, original_port),
|
||||
// protocol_version,
|
||||
// )
|
||||
// .await
|
||||
|
||||
async fn get_server_status_new(
|
||||
address: &str,
|
||||
protocol_version: Option<ProtocolVersion>,
|
||||
) -> Result<ServerStatus> {
|
||||
let (original_host, original_port) = parse_server_address(address)?;
|
||||
let (host, port) =
|
||||
resolve_server_address(original_host, original_port).await?;
|
||||
tracing::debug!(
|
||||
"Pinging {address} with protocol version {protocol_version:?}"
|
||||
);
|
||||
server_ping::get_server_status(
|
||||
&(&host as &str, port),
|
||||
(original_host, original_port),
|
||||
protocol_version,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn _get_server_status_new(
|
||||
address: &str,
|
||||
protocol_version: Option<ProtocolVersion>,
|
||||
) -> Result<ServerStatus> {
|
||||
let (address, port) = match address.rsplit_once(':') {
|
||||
Some((addr, port)) => {
|
||||
let port = port.parse::<u16>().map_err(|_err| {
|
||||
Error::from(ErrorKind::InputError("invalid port number".into()))
|
||||
})?;
|
||||
(addr, port)
|
||||
}
|
||||
None => (address, 25565),
|
||||
};
|
||||
|
||||
let mut builder = async_minecraft_ping::ConnectionConfig::build(address)
|
||||
.with_port(port)
|
||||
.with_srv_lookup();
|
||||
|
||||
if let Some(version) = protocol_version {
|
||||
builder = builder.with_protocol_version(version.version as usize)
|
||||
}
|
||||
|
||||
let conn = builder.connect().await.map_err(|_err| {
|
||||
Error::from(ErrorKind::InputError("failed to connect to server".into()))
|
||||
})?;
|
||||
let conn = builder
|
||||
.connect()
|
||||
.await
|
||||
.wrap_err("failed to connect to server")?;
|
||||
|
||||
let ping_conn = conn.status().await.map_err(|_err| {
|
||||
Error::from(ErrorKind::InputError("failed to get server status".into()))
|
||||
})?;
|
||||
let ping_conn = conn
|
||||
.status()
|
||||
.await
|
||||
.wrap_err("failed to get server status")?;
|
||||
let status = &ping_conn.status;
|
||||
let description = match &status.description {
|
||||
ServerDescription::Plain(text) => {
|
||||
serde_json::value::to_raw_value(&text).ok()
|
||||
}
|
||||
ServerDescription::Object { text } => {
|
||||
// TODO: `text` always seems to be empty?
|
||||
RawValue::from_string(text.clone()).ok()
|
||||
}
|
||||
};
|
||||
let description = status.description.as_ref().map(|d| {
|
||||
let json =
|
||||
serde_json::to_string(d).expect("serializing should not fail");
|
||||
RawValue::from_string(json)
|
||||
.expect("converting to `RawValue` should not fail")
|
||||
});
|
||||
|
||||
let players = ServerPlayers {
|
||||
max: status.players.max,
|
||||
@@ -1000,9 +986,10 @@ async fn _get_server_status_new(
|
||||
let latency = {
|
||||
let start = Instant::now();
|
||||
let ping_magic = Utc::now().timestamp_millis().cast_unsigned();
|
||||
ping_conn.ping(ping_magic).await.map_err(|_err| {
|
||||
Error::from(ErrorKind::InputError("failed to do ping".into()))
|
||||
})?;
|
||||
ping_conn
|
||||
.ping(ping_magic)
|
||||
.await
|
||||
.wrap_err("failed to do ping")?;
|
||||
start.elapsed().as_millis() as i64
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
//! Theseus error type
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt::{Debug, Display},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{profile, util};
|
||||
use data_url::DataUrlError;
|
||||
@@ -220,4 +224,41 @@ impl ErrorKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||
|
||||
pub trait Context<T, E>: Sized {
|
||||
fn wrap_err_with<D>(self, f: impl FnOnce() -> D) -> Result<T, Error>
|
||||
where
|
||||
D: Send + Sync + Debug + Display + 'static;
|
||||
|
||||
#[inline]
|
||||
fn wrap_err<D>(self, msg: D) -> Result<T, Error>
|
||||
where
|
||||
D: Send + Sync + Debug + Display + 'static,
|
||||
{
|
||||
self.wrap_err_with(|| msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Context<T, E> for Result<T, E>
|
||||
where
|
||||
Self: eyre::WrapErr<T, E>,
|
||||
{
|
||||
fn wrap_err_with<D>(self, f: impl FnOnce() -> D) -> Result<T, Error>
|
||||
where
|
||||
D: Send + Sync + Debug + Display + 'static,
|
||||
{
|
||||
eyre::WrapErr::wrap_err_with(self, f).map_err(|err| {
|
||||
Error::from(ErrorKind::OtherError(format!("{err:#}")))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Context<T, Infallible> for Option<T> {
|
||||
fn wrap_err_with<D>(self, f: impl FnOnce() -> D) -> Result<T, Error>
|
||||
where
|
||||
D: Send + Sync + Debug + Display + 'static,
|
||||
{
|
||||
self.ok_or_else(|| Error::from(ErrorKind::OtherError(f().to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ macro_rules! get_resource_file {
|
||||
Ok(dir) => dir,
|
||||
Err(e) => {
|
||||
break 'get_resource_file $crate::Result::Err(
|
||||
$crate::util::io::IOError::from(e).into(),
|
||||
$crate::Error::from($crate::util::io::IOError::from(e)),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,53 +1,3 @@
|
||||
use crate::ErrorKind;
|
||||
use crate::error::Result;
|
||||
use crate::util::protocol_version::ProtocolVersion;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
use std::time::Duration;
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tokio::select;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServerStatus {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<Box<RawValue>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub players: Option<ServerPlayers>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<ServerVersion>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub favicon: Option<Url>,
|
||||
#[serde(default)]
|
||||
pub enforces_secure_chat: bool,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ping: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerPlayers {
|
||||
pub max: i32,
|
||||
pub online: i32,
|
||||
#[serde(default)]
|
||||
pub sample: Vec<ServerGameProfile>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerGameProfile {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerVersion {
|
||||
pub name: String,
|
||||
pub protocol: i32,
|
||||
#[serde(skip_deserializing)]
|
||||
pub legacy: bool,
|
||||
}
|
||||
|
||||
pub async fn get_server_status(
|
||||
address: &impl ToSocketAddrs,
|
||||
original_address: (&str, u16),
|
||||
@@ -305,8 +255,8 @@ mod legacy {
|
||||
}),
|
||||
description: parts.next().and_then(|x| to_raw_value(x).ok()),
|
||||
players: Some(ServerPlayers {
|
||||
online: parts.next().and_then(|x| x.parse().ok()).unwrap_or(-1),
|
||||
max: parts.next().and_then(|x| x.parse().ok()).unwrap_or(-1),
|
||||
online: parts.next().and_then(|x| x.parse().ok()),
|
||||
max: parts.next().and_then(|x| x.parse().ok()),
|
||||
sample: vec![],
|
||||
}),
|
||||
favicon: None,
|
||||
43
packages/app-lib/src/util/server_ping/mod.rs
Normal file
43
packages/app-lib/src/util/server_ping/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ServerStatus {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<Box<RawValue>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub players: Option<ServerPlayers>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<ServerVersion>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub favicon: Option<Url>,
|
||||
#[serde(default)]
|
||||
pub enforces_secure_chat: bool,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ping: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerPlayers {
|
||||
pub max: Option<i32>,
|
||||
pub online: Option<i32>,
|
||||
#[serde(default)]
|
||||
pub sample: Vec<ServerGameProfile>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerGameProfile {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct ServerVersion {
|
||||
pub name: String,
|
||||
pub protocol: i32,
|
||||
#[serde(skip_deserializing)]
|
||||
pub legacy: bool,
|
||||
}
|
||||
Reference in New Issue
Block a user