Skip to content

Commit ab0db9d

Browse files
authored
Feature: 在加入房间时展示 p2p 连接难度 (#105)
* Feature: Display connection difficulty based on NAT status in guest-connecting state. * Fix: typo
1 parent ab25fa0 commit ab0db9d

File tree

8 files changed

+121
-38
lines changed

8 files changed

+121
-38
lines changed

src/controller/api.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::sync::mpsc;
22
use crate::controller::states::AppState;
3-
use crate::controller::{experimental, ExceptionType, Room};
3+
use crate::controller::{experimental, ConnectionDifficulty, ExceptionType, Room};
44
use crate::scaffolding::profile::Profile;
55
use crate::mc::scanning::MinecraftScanner;
66
use crate::MOTD;
@@ -48,8 +48,14 @@ pub fn get_state() -> Value {
4848
AppState::GuestConnecting { room, .. } => {
4949
json!({"state": "guest-connecting", "index": index, "room": room.code})
5050
}
51-
AppState::GuestStarting { room, .. } => {
52-
json!({"state": "guest-starting", "index": index, "room": room.code})
51+
AppState::GuestStarting { room, difficulty, .. } => {
52+
json!({"state": "guest-starting", "index": index, "room": room.code, "difficulty": match difficulty {
53+
ConnectionDifficulty::Unknown => "UNKNOWN",
54+
ConnectionDifficulty::Easiest => "EASIEST",
55+
ConnectionDifficulty::Simple => "SIMPLE",
56+
ConnectionDifficulty::Medium => "MEDIUM",
57+
ConnectionDifficulty::Tough => "TOUGH",
58+
}})
5359
}
5460
AppState::GuestOk { server, profiles, .. } => {
5561
let url = if server.port == 25565 {

src/controller/rooms/experimental/room.rs

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::controller::experimental::{MACHINE_ID, VENDOR};
22
use crate::controller::rooms::legacy;
33
use crate::controller::states::{AppState, AppStateCapture};
4-
use crate::controller::{ExceptionType, Room, RoomKind, SCAFFOLDING_PORT};
4+
use crate::controller::{ConnectionDifficulty, ExceptionType, Room, RoomKind, SCAFFOLDING_PORT};
55
use crate::easytier;
66
use crate::easytier::argument::{Argument, PortForward, Proto};
77
use crate::easytier::publics::{fetch_public_nodes, PublicServers};
@@ -226,7 +226,7 @@ pub fn start_guest(room: Room, player: Option<String>, capture: AppStateCapture)
226226
return;
227227
};
228228

229-
state.set(AppState::GuestStarting { room, easytier })
229+
state.set(AppState::GuestStarting { room, easytier, difficulty: ConnectionDifficulty::Unknown })
230230
};
231231

232232
let (scaffolding_port, host_ip) = 'local_port: {
@@ -237,7 +237,7 @@ pub fn start_guest(room: Room, player: Option<String>, capture: AppStateCapture)
237237
return;
238238
};
239239
let mut state = state.into_slow();
240-
let AppState::GuestStarting { easytier, .. } = state.as_mut_ref() else {
240+
let AppState::GuestStarting { easytier, difficulty, .. } = state.as_mut_ref() else {
241241
unreachable!();
242242
};
243243
if !easytier.is_alive() {
@@ -248,25 +248,46 @@ pub fn start_guest(room: Room, player: Option<String>, capture: AppStateCapture)
248248
let Some(players) = easytier.get_players() else {
249249
continue;
250250
};
251-
for EasyTierMember { hostname, address } in players {
252-
if hostname.starts_with("scaffolding-mc-server-") && let Ok(port) = u16::from_str(&hostname["scaffolding-mc-server-".len()..]) {
253-
logging!("RoomExperiment", "Scaffolding Server is at {}:{}", address, port);
254-
255-
let local_port = PortRequest::Scaffolding.request();
256-
257-
if !easytier.add_port_forward(&[PortForward {
258-
local: SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, local_port).into(),
259-
remote: SocketAddrV4::new(address, port).into(),
260-
proto: Proto::TCP,
261-
}]) {
262-
logging!("RoomExperiment", "Cannot create a port-forward {} -> {} for Scaffolding Connection.", local_port, port);
263-
state.set(AppState::Exception { kind: ExceptionType::GuestEasytierCrash });
264-
return;
265-
};
266251

267-
break 'local_port (local_port, address);
252+
let Some(local_nat) = players.iter().find_map(|EasyTierMember { is_local, nat, ..}| {
253+
if *is_local {
254+
Some(nat)
255+
} else {
256+
None
268257
}
269-
}
258+
}) else {
259+
continue;
260+
};
261+
262+
let Some((server_address, server_port, server_nat)) = players.iter().find_map(|
263+
EasyTierMember { hostname, address, is_local, nat, .. }
264+
| {
265+
static PREFIX: &str = "scaffolding-mc-server-";
266+
267+
if let Some(address) = address && !is_local && hostname.starts_with(PREFIX) && let Ok(port) = u16::from_str(&hostname[PREFIX.len()..]) {
268+
Some((address, port, nat))
269+
} else {
270+
None
271+
}
272+
}) else {
273+
continue;
274+
};
275+
276+
logging!("RoomExperiment", "Scaffolding Server is at {}:{}", server_address, server_port);
277+
let local_port = PortRequest::Scaffolding.request();
278+
if !easytier.add_port_forward(&[PortForward {
279+
local: SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, local_port).into(),
280+
remote: SocketAddrV4::new(*server_address, server_port).into(),
281+
proto: Proto::TCP,
282+
}]) {
283+
logging!("RoomExperiment", "Cannot create a port-forward {} -> {} for Scaffolding Connection.", local_port, server_port);
284+
state.set(AppState::Exception { kind: ExceptionType::GuestEasytierCrash });
285+
return;
286+
};
287+
288+
*difficulty = easytier::calc_conn_difficulty(local_nat, server_nat);
289+
logging!("RoomExperiment", "Current NAT status: {:?} -> {:?}, difficulty = {:?}", local_nat, server_nat, difficulty);
290+
break 'local_port (local_port, *server_address);
270291
}
271292

272293
logging!("RoomExperiment", "Cannot find scaffolding server.");
@@ -401,7 +422,7 @@ pub fn start_guest(room: Room, player: Option<String>, capture: AppStateCapture)
401422
return;
402423
};
403424
state.replace(|state| {
404-
let AppState::GuestStarting { room, easytier } = state else {
425+
let AppState::GuestStarting { room, easytier, .. } = state else {
405426
unreachable!();
406427
};
407428

src/controller/rooms/legacy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::controller::states::{AppState, AppStateCapture};
2-
use crate::controller::{ExceptionType, Room, RoomKind};
2+
use crate::controller::{ConnectionDifficulty, ExceptionType, Room, RoomKind};
33
use crate::easytier;
44
use crate::easytier::argument::{Argument, PortForward, Proto};
55
use crate::mc::fakeserver::FakeServer;
@@ -217,7 +217,7 @@ pub fn start_guest(room: Room, capture: AppStateCapture) {
217217
let Some(state) = capture.try_capture() else {
218218
return;
219219
};
220-
state.set(AppState::GuestStarting { room, easytier })
220+
state.set(AppState::GuestStarting { room, easytier, difficulty: ConnectionDifficulty::Unknown })
221221
};
222222

223223
'init_conn: {
@@ -248,7 +248,7 @@ pub fn start_guest(room: Room, capture: AppStateCapture) {
248248
};
249249

250250
state.replace(move |state| match state {
251-
AppState::GuestStarting { room, easytier } => AppState::GuestOk {
251+
AppState::GuestStarting { room, easytier, .. } => AppState::GuestOk {
252252
room,
253253
easytier,
254254
server,

src/controller/rooms/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ pub(crate) enum RoomKind {
2020
PCL2CE { mc_port: u16 },
2121
}
2222

23+
#[derive(Debug)]
24+
pub enum ConnectionDifficulty {
25+
Unknown, Easiest, Simple, Medium, Tough
26+
}
27+
2328
impl Room {
2429
pub fn create() -> Room {
2530
experimental::create_room()

src/controller/states.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::mem;
66
use std::panic::Location;
77
use std::time::{Duration, SystemTime};
88
use parking_lot::{Mutex, MutexGuard};
9-
use crate::controller::Room;
9+
use crate::controller::{ConnectionDifficulty, Room};
1010
use crate::scaffolding::profile::Profile;
1111

1212
pub enum AppState {
@@ -32,6 +32,7 @@ pub enum AppState {
3232
GuestStarting {
3333
room: Room,
3434
easytier: EasyTier,
35+
difficulty: ConnectionDifficulty
3536
},
3637
GuestOk {
3738
room: Room,
@@ -61,8 +62,8 @@ impl Debug for AppState {
6162
AppState::GuestConnecting { room } => {
6263
write!(f, "AppState::GuestConnecting {{ code: {:?} }}", room.code)
6364
}
64-
AppState::GuestStarting { room, .. } => {
65-
write!(f, "AppState::GuestStarting {{ code: {:?}, easytier: .. }}", room.code)
65+
AppState::GuestStarting { room, difficulty, .. } => {
66+
write!(f, "AppState::GuestStarting {{ code: {:?}, difficulty: {:?}, easytier: .. }}", room.code, difficulty)
6667
}
6768
AppState::GuestOk { room, server, profiles, .. } => {
6869
write!(

src/easytier/argument.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ use std::net::{Ipv4Addr, SocketAddr};
33

44
type CowString = Cow<'static, str>;
55

6-
#[derive(Clone)]
6+
#[derive(Clone, Debug)]
77
pub struct PortForward {
88
pub(crate) local: SocketAddr,
99
pub(crate) remote: SocketAddr,
1010
pub(crate) proto: Proto,
1111
}
1212

13-
#[derive(Clone)]
13+
#[derive(Clone, Debug)]
1414
pub enum Proto {
1515
TCP,
1616
UDP,

src/easytier/executable_impl.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::{
1515
thread,
1616
time::Duration,
1717
};
18-
use crate::easytier::EasyTierMember;
18+
use crate::easytier::{EasyTierMember, NatType};
1919

2020
static EASYTIER_ARCHIVE: (&str, &str, &[u8]) = (
2121
include_str!(env!("TERRACOTTA_ET_ENTRY_CONF")),
@@ -230,11 +230,26 @@ impl EasyTier {
230230
let mut players: Vec<EasyTierMember> = vec![];
231231
for item in object.as_array()? {
232232
let hostname = item.as_object()?.get("hostname")?.as_str()?.to_string();
233-
let Ok(address) = Ipv4Addr::from_str(item.as_object()?.get("ipv4")?.as_str()?) else {
234-
continue;
233+
let address = Ipv4Addr::from_str(item.as_object()?.get("ipv4")?.as_str()?).ok();
234+
let is_local = item.as_object()?.get("cost")?.as_str()? == "Local";
235+
let nat = match item.as_object()?.get("nat_type")?.as_str()? {
236+
"Unknown" => NatType::Unknown,
237+
"OpenInternet" => NatType::OpenInternet,
238+
"NoPat" => NatType::NoPAT ,
239+
"FullCone" => NatType::FullCone,
240+
"Restricted" => NatType::Restricted,
241+
"PortRestricted" => NatType::PortRestricted,
242+
"Symmetric" => NatType::Symmetric,
243+
"SymUdpFirewall" => NatType::SymmetricUdpWall,
244+
"SymmetricEasyInc" => NatType::SymmetricEasyIncrease,
245+
"SymmetricEasyDec" => NatType::SymmetricEasyDecrease,
246+
#[cfg(debug_assertions)]
247+
nat => panic!("Unknown NAT type: {}", nat),
248+
#[cfg(not(debug_assertions))]
249+
_ => return None,
235250
};
236251

237-
players.push(EasyTierMember { hostname, address });
252+
players.push(EasyTierMember { hostname, address, is_local, nat });
238253
}
239254
Some(players)
240255
}

src/easytier/mod.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use std::net::Ipv4Addr;
1+
use crate::controller::ConnectionDifficulty;
22
use crate::easytier::argument::{Argument, PortForward};
33
use cfg_if::cfg_if;
4+
use std::cmp::PartialEq;
5+
use std::net::Ipv4Addr;
46

57
pub mod argument;
68
pub mod publics;
@@ -14,9 +16,42 @@ cfg_if! {
1416

1517
pub struct EasyTier(inner::EasyTier);
1618

19+
#[derive(Debug)]
1720
pub struct EasyTierMember {
1821
pub hostname: String,
19-
pub address: Ipv4Addr
22+
pub address: Option<Ipv4Addr>,
23+
pub is_local: bool,
24+
pub nat: NatType,
25+
}
26+
27+
#[derive(Clone, Debug, PartialEq)]
28+
pub enum NatType {
29+
Unknown,
30+
OpenInternet,
31+
NoPAT,
32+
FullCone,
33+
Restricted,
34+
PortRestricted,
35+
Symmetric,
36+
SymmetricUdpWall,
37+
SymmetricEasyIncrease,
38+
SymmetricEasyDecrease,
39+
}
40+
41+
pub fn calc_conn_difficulty(left: &NatType, right: &NatType) -> ConnectionDifficulty {
42+
let is = |types: &[NatType]| -> bool {
43+
types.contains(left) || types.contains(right)
44+
};
45+
46+
if is(&[NatType::OpenInternet]) {
47+
ConnectionDifficulty::Easiest
48+
} else if is(&[NatType::NoPAT,NatType::FullCone]) {
49+
ConnectionDifficulty::Simple
50+
} else if is(&[NatType::Restricted, NatType::PortRestricted]) {
51+
ConnectionDifficulty::Medium
52+
} else {
53+
ConnectionDifficulty::Tough
54+
}
2055
}
2156

2257
pub fn create(args: Vec<Argument>) -> EasyTier {

0 commit comments

Comments
 (0)