~starkingdoms/starkingdoms

ref: 49ce5c955d6a5ab742a7065584b4012240e51c6a starkingdoms/client/src/lib.rs -rw-r--r-- 4.0 KiB
49ce5c95 — core SLP 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use std::error::Error;
use futures::stream::{SplitSink, SplitStream};
use futures::StreamExt;
use log::{debug, error, info, Level, trace, warn};
use wasm_bindgen::prelude::*;
use ws_stream_wasm::{WsMessage, WsMeta, WsStream};
use starkingdoms_protocol::State;
use starkingdoms_protocol::PROTOCOL_VERSION;
use starkingdoms_protocol::MessageS2C;
use starkingdoms_protocol::MessageC2S;
use futures::SinkExt;
use lazy_static::lazy_static;
use std::sync::Arc;
use std::sync::RwLock;
use futures::FutureExt;

#[macro_use]
pub mod macros;
pub mod chat;

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub async fn rust_init(gateway: &str, username: &str) -> Result<(), JsError> {
    console_log::init_with_level(Level::Debug).unwrap();

    info!("Logger setup successfully");

    match main(gateway, username).await {
        Ok(c) => c,
        Err(e) => {
            error!("Error initializing gateway client: {}", e);
            return Err(JsError::new(&e.to_string()));
        }
    };

    info!("Gateway client set up successfully");

    Ok(())
}

pub struct Client {
    pub client_data: Option<ClientData>,
}

pub struct ClientData {
    pub state: State,
    pub tx: SplitSink<WsStream, WsMessage>,
    pub rx: SplitStream<WsStream>
}

lazy_static! {
    pub static ref CLIENT: Arc<RwLock<Client>> = Arc::new(RwLock::new(Client {
        client_data: None
    }));
}

pub async fn main(gateway: &str, username: &str) -> Result<(), Box<dyn Error>> {
    info!("FAST CONNECT: {}", gateway);
    let gateway_url = url::Url::parse(gateway)?;
    trace!("Gateway URL parsed");
    let (_ws, ws_stream) = WsMeta::connect(gateway_url, None).await?;
    trace!("Connected to gateway socket");
    let (tx, rx) = ws_stream.split();

    let mut client_data = ClientData {
        state: State::Handshake,
        tx,
        rx
    };

    trace!("Split stream, handshaking with server");

    send!(client_data.tx, &MessageC2S::Hello {
        next_state: State::Play,
        version: PROTOCOL_VERSION,
        requested_username: username.to_string()
    }).await?;

    trace!("Sent handshake start packet");

    if let Some(msg) = recv_now!(client_data.rx)? {
        let typed_msg: MessageS2C = msg;

        match typed_msg {
            MessageS2C::Hello { version, given_username, next_state } => {
                info!("FAST CONNECT - connected to server protocol {} given username {}, switching to state {:?}", version, given_username, next_state);
                client_data.state = next_state;
            },
            MessageS2C::Goodbye { reason } => {
                error!("server disconnected before finishing handshake: {:?}", reason);
                return Err(format!("disconnected by server: {:?}", reason).into());
            },
            _ => {
                warn!("received unexpected packet from server: {:?}", typed_msg);
            }
        }
    } else {
        error!("Server closed the connection")
    }

    CLIENT.write()?.client_data = Some(client_data);

    Ok(())
}

#[wasm_bindgen]
pub async fn update_socket() -> Result<(), JsError> {
    let mut client = CLIENT.write()?;

    if client.client_data.is_none() {
        return Err(JsError::new("Client not yet initialized"));
    }

    let client_data = client.client_data.as_mut().unwrap();

    let maybe_msg: Option<MessageS2C> = match recv!(client_data.rx) {
        Ok(r) => r,
        Err(e) => {
            return Err(JsError::new(e))
        }
    };

    if let Some(msg) = maybe_msg {
        match msg {
            MessageS2C::Goodbye { reason } => {
                info!("server sent disconnect: {:?}", reason);
                client.client_data = None;
                return Err(JsError::new("disconnected by server"));
            }
            MessageS2C::Chat { from, message } => {
                info!("[CHAT] {}: {}", from, message);
                // TODO: Handle
            },
            _ => {
                warn!("server sent unexpected packet {:?}, ignoring", msg);
            }
        }
    }

    Ok(())
}