Skip to main content

flatland_client_lib/
connect.rs

1//! TCP session connect for remote flatland-server.
2
3use std::net::SocketAddr;
4
5use flatland_protocol::{AuthCredential, Hello, PROTOCOL_VERSION};
6use uuid::Uuid;
7
8use crate::client_config::default_game_server_addr;
9use crate::remote::{self, RemoteSession};
10
11pub use remote::connect_tcp;
12
13#[derive(Debug, Clone)]
14pub struct ConnectOptions {
15    pub name: String,
16    pub server: SocketAddr,
17    pub auth: AuthCredential,
18    pub character_id: Option<Uuid>,
19}
20
21impl ConnectOptions {
22    pub fn to_server(name: impl Into<String>, server: SocketAddr) -> Self {
23        Self {
24            name: name.into(),
25            server,
26            auth: AuthCredential::DevLocal,
27            character_id: None,
28        }
29    }
30
31    pub fn with_auth(mut self, auth: AuthCredential, character_id: Option<Uuid>) -> Self {
32        self.auth = auth;
33        self.character_id = character_id;
34        self
35    }
36
37    pub fn hello(&self) -> Hello {
38        Hello {
39            client_name: self.name.clone(),
40            protocol_version: PROTOCOL_VERSION,
41            auth: self.auth.clone(),
42            character_id: self.character_id,
43        }
44    }
45}
46
47pub fn default_server_addr() -> SocketAddr {
48    default_game_server_addr()
49}
50
51pub async fn connect(opts: ConnectOptions) -> anyhow::Result<RemoteSession> {
52    connect_tcp(opts.server, &opts.hello()).await
53}
54
55/// Wire / log token for a character that already has a live session.
56pub const CHARACTER_ALREADY_CONNECTED: &str = "character_already_connected";
57
58/// Human-facing text for [`CHARACTER_ALREADY_CONNECTED`] (and legacy kick reasons).
59pub fn connect_reject_message(reason: &str) -> String {
60    if is_character_busy_connect_error(reason) {
61        "Character already connected — close the other client or choose a different character."
62            .into()
63    } else {
64        format!("Connect rejected: {reason}")
65    }
66}
67
68/// True when connect/reconnect should stop auto-retrying (character exclusive).
69pub fn is_character_busy_connect_error(err: &str) -> bool {
70    let lower = err.to_ascii_lowercase();
71    lower.contains(CHARACTER_ALREADY_CONNECTED)
72        || lower.contains("character already connected")
73        || lower.contains("character_connected_elsewhere")
74}