~starkingdoms/starkingdoms

ref: 4365b12e27955e51bfeb114caed69f8f52f26bd4 starkingdoms/client/src/lib.rs -rw-r--r-- 4.4 KiB
4365b12e — c0repwn3r ultra basic chat ui 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
146
147
148
149
150
151
152
153
154
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 protocol::State;
use protocol::PROTOCOL_VERSION;
use protocol::MessageS2C;
use protocol::MessageC2S;
use futures::SinkExt;
use lazy_static::lazy_static;
use std::sync::Arc;
use std::sync::RwLock;
use futures::FutureExt;
use web_sys::Window;

#[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);

                let window: Window = web_sys::window().expect("no global `window` exists");
                let document = window.document().expect("should have a document on window");
                let chatbox = document.get_element_by_id("chats").expect("chatbox does not exist");

                let new_elem = document.create_element("p").expect("could not create element");
                new_elem.set_inner_html(&format!("<b>{}</b>: {}", from, message));

                chatbox.append_child(&new_elem).unwrap();
            },
            _ => {
                warn!("server sent unexpected packet {:?}, ignoring", msg);
            }
        }
    }

    Ok(())
}