multiplayer/
multiplayer.rs1use sge::prelude::*;
2use ui::*;
3
4#[derive(Clone)]
5#[persistent(diff, lerp)]
6struct State {
7 position: Vec2,
8}
9
10const NAMES: &[&str] = &["Alice", "Bob", "Charlie", "Derek", "Emily", "Fred", "Gurt"];
11const UPDATE_RATE: f32 = 0.1;
12const INTERPOLATION_DELAY: f32 = UPDATE_RATE * 2.0;
13
14#[main("Multiplayer")]
15fn main() {
16 set_min_log_level(LevelFilter::Debug);
17
18 let mut state = MultiplayerState::new(
19 State {
20 position: Vec2::ZERO,
21 },
22 rand_choice(NAMES).to_string(),
23 "example_room".to_string(),
24 );
25
26 loop {
27 clear_screen(Color::NEUTRAL_900);
28 let now = time();
29
30 if once_per_n_seconds(UPDATE_RATE) {
31 state.update().unwrap();
32 }
33
34 let movement =
35 pressed_movement_vector(KeyCode::KeyW, KeyCode::KeyS, KeyCode::KeyA, KeyCode::KeyD);
36 state.your_state_mut().position += movement * delta_time() * 300.0;
37
38 draw_user(
39 state.your_username().to_string(),
40 state.your_state().position,
41 );
42
43 let render_target_time = now - INTERPOLATION_DELAY;
44 for (_, user) in state.other_users().iter() {
45 if let Some(interpolated_state) = user.current_lerped(render_target_time) {
46 draw_user(user.username.clone(), interpolated_state.position);
47 }
48 }
49
50 chat(&mut state);
51
52 draw_logs();
53
54 if should_quit() {
55 break;
56 }
57 next_frame().await;
58 }
59
60 state.disconnect();
61 std::thread::sleep(Duration::from_millis(50));
63}
64
65struct ChatState {
66 messages: Vec<RichText>,
67}
68
69fn chat(state: &mut MultiplayerState<State>) {
70 if !storage_exists::<ChatState>() {
71 storage_store_state(ChatState { messages: vec![] });
72 }
73
74 let messages = &mut storage_get_state_mut::<ChatState>().messages;
75
76 let other_state = unsafe { &*{ state as *const MultiplayerState<State> } };
77 for notification in state.drain_notifications() {
78 let Some(user) = other_state.get_user(notification.user_id) else {
79 break;
80 };
81 let username = &user.username;
82 let text = String::from_utf8(notification.data).unwrap();
83
84 messages.push(rich_text(format!("<b>{username}</b>: {text}")).unwrap());
85 }
86
87 let input_id = id!();
88 let button_id = id!();
89
90 if button_clicked_last_frame(button_id) {
91 let input_state = text_input_state(input_id);
92 messages.push(rich_text(format!("<b>You</b>: {}", input_state.value)).unwrap());
93 state.send_notification(input_state.value.as_bytes().to_vec());
94 input_state.value = "".to_string();
95 }
96
97 let ui = Align::bottom_left(
98 BoxFill::new(
99 flat::BG0,
100 Padding::all(
101 20.0,
102 Col::new([
103 FlexRow::with_gap(
104 10.0,
105 [
106 FlexBox::Flex(flat::TextInput::new(flat::BG1, input_id)),
107 FlexBox::Fixed(flat::Button::text(
108 flat::BG1,
109 flat::BG2,
110 button_id,
111 "Send",
112 )),
113 ],
114 ),
115 Scroll::new(
116 id!(),
117 Col::with_gap(
118 10.0,
119 messages
120 .iter()
121 .map(|r| RichTextNode::new(r.clone()))
122 .collect::<Vec<_>>(),
123 ),
124 )
125 .padding_vertical(20.0),
126 ]),
127 ),
128 )
129 .sized_wh(600.0, 400.0),
130 )
131 .sized(window_size());
132
133 draw_ui(ui, Vec2::ZERO);
134}
135
136fn draw_user(username: String, pos: Vec2) {
137 draw_circle_world(pos, 50.0, Color::BLUE_500);
138 draw_text_world(&username, pos);
139}