1use crate::mc::{
2 Description, DisconnectResponse, MinecraftPacket, Players, StatusResponse, Version,
3};
4use crate::{Config, UserHistory, WakeupCondition};
5use dashmap::DashMap;
6use std::sync::Arc;
7use std::sync::atomic::Ordering;
8use std::time::{Duration, Instant};
9use tokio::io::AsyncReadExt;
10use tokio::net::TcpStream;
11use tokio::time::sleep;
12
13pub struct McHandler;
14
15impl McHandler {
16 pub async fn send_fallback(
17 socket: &mut TcpStream,
18 state: i32,
19 cfg: Arc<Config>,
20 history: Arc<DashMap<String, UserHistory>>,
21 ip: String,
22 ) -> tokio::io::Result<()> {
23 if cfg.on_wakeup.is_none() {
24 return Ok(());
25 }
26
27 let should_trigger = match cfg.wakeup_on {
28 WakeupCondition::Disabled => false,
29 WakeupCondition::Motd => state == 1 || state == 2,
30 WakeupCondition::Join => state == 2,
31 };
32
33 if should_trigger {
34 Self::trigger_wakeup(Arc::clone(&cfg)).await;
35 }
36
37 if state == 1 {
38 Self::handle_wakeup(socket, cfg).await
39 } else {
40 let attempts = {
41 let mut entry = history.entry(ip).or_insert(UserHistory {
42 attempts: 0,
43 last_seen: Instant::now(),
44 });
45
46 if entry.last_seen.elapsed() > Duration::from_secs(300) {
47 entry.attempts = 1;
48 } else {
49 entry.attempts += 1;
50 }
51 entry.last_seen = Instant::now();
52 entry.attempts
53 };
54
55 if attempts >= 3 {
56 Self::handle_waitlist(socket, cfg).await
57 } else {
58 Self::handle_disconnect(socket, attempts, cfg).await
59 }
60 }
61 }
62
63 async fn handle_wakeup(socket: &mut TcpStream, cfg: Arc<Config>) -> tokio::io::Result<()> {
64 let response = StatusResponse {
65 version: Version {
66 name: "mc-gate".to_string(),
67 protocol: 767,
68 },
69 players: Players {
70 max: 0,
71 online: "???".to_string(),
72 },
73 description: Description {
74 text: cfg.msg_motd.clone(),
75 },
76 };
77
78 MinecraftPacket::send_json(socket, 0x00, &response).await?;
79
80 let mut buf = [0u8; 32];
81 if let Ok(Ok(n)) =
82 tokio::time::timeout(Duration::from_secs(2), socket.read(&mut buf[..])).await
83 {
84 let start = Instant::now();
85 while start.elapsed().as_secs() < 120 {
86 if TcpStream::connect(&cfg.mc).await.is_ok() {
87 tokio::io::AsyncWriteExt::write_all(socket, &buf[..n]).await?;
88 return Ok(());
89 }
90 sleep(Duration::from_secs(1)).await;
91 }
92 }
93 Ok(())
94 }
95
96 async fn handle_waitlist(socket: &mut TcpStream, cfg: Arc<Config>) -> tokio::io::Result<()> {
97 let start = Instant::now();
98 while start.elapsed().as_secs() < 28 {
99 if TcpStream::connect(&cfg.mc).await.is_ok() {
100 let res = DisconnectResponse {
101 text: cfg.msg_online.clone(),
102 };
103 return MinecraftPacket::send_json(socket, 0x00, &res).await;
104 }
105 sleep(Duration::from_millis(800)).await;
106 }
107
108 let res = DisconnectResponse {
109 text: cfg.msg_timeout.clone(),
110 };
111 MinecraftPacket::send_json(socket, 0x00, &res).await
112 }
113
114 async fn handle_disconnect(socket: &mut TcpStream, attempts: u32, cfg: Arc<Config>) -> tokio::io::Result<()> {
115 sleep(Duration::from_millis(10)).await;
116 let text = if attempts == 1 {
117 &cfg.msg_starting
118 } else {
119 &cfg.msg_waitlist
120 }.to_string();
121
122 MinecraftPacket::send_json(socket, 0x00, &DisconnectResponse { text }).await
123 }
124
125 async fn trigger_wakeup(cfg: Arc<Config>) {
126 if let Some(callback) = &cfg.on_wakeup {
127 if cfg
128 .is_waking
129 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
130 .is_err()
131 {
132 return;
133 }
134
135 let cb = Arc::clone(callback);
136 tokio::spawn(async move {
137 cb().await;
138 });
139 }
140 }
141}