34 BEGIN_ZFC_NAMESPACE |
34 BEGIN_ZFC_NAMESPACE |
35 |
35 |
36 // ------------------------------------------------------------------------------------------------- |
36 // ------------------------------------------------------------------------------------------------- |
37 // |
37 // |
38 RCONSession::RCONSession() : |
38 RCONSession::RCONSession() : |
39 m_state (RCON_DISCONNECTED), |
39 m_state(RCON_DISCONNECTED), |
40 m_lastPing (0), |
40 m_lastPing(0), |
41 m_numAdmins (0), |
41 m_adminCount(0), |
42 m_interface (nullptr) |
42 m_interface(nullptr) |
43 { |
43 { |
44 if (not m_socket.set_blocking (false)) |
44 if (not m_socket.set_blocking(false)) |
45 { |
45 { |
46 fprintf (stderr, "unable to set socket as non-blocking: %s\n", |
46 fprintf(stderr, "unable to set socket as non-blocking: %s\n", |
47 m_socket.error_string().chars()); |
47 m_socket.error_string().chars()); |
48 exit (EXIT_FAILURE); |
48 exit(EXIT_FAILURE); |
49 } |
49 } |
50 } |
50 } |
51 |
51 |
52 // ------------------------------------------------------------------------------------------------- |
52 // ------------------------------------------------------------------------------------------------- |
53 // |
53 // |
54 RCONSession::~RCONSession() {} |
54 RCONSession::~RCONSession() {} |
55 |
55 |
56 // ------------------------------------------------------------------------------------------------- |
56 // ------------------------------------------------------------------------------------------------- |
57 // |
57 // |
58 void RCONSession::connect (IPAddress address) |
58 void RCONSession::connect(IPAddress address) |
59 { |
59 { |
60 m_address = address; |
60 m_address = address; |
61 m_state = RCON_CONNECTING; |
61 m_state = RCON_CONNECTING; |
62 m_interface->updateStatusBar(); |
62 m_interface->updateStatusBar(); |
63 send_hello(); |
63 sendHello(); |
64 } |
64 } |
65 |
65 |
66 // ------------------------------------------------------------------------------------------------- |
66 // ------------------------------------------------------------------------------------------------- |
67 // |
67 // |
68 void RCONSession::disconnect() |
68 void RCONSession::disconnect() |
69 { |
69 { |
70 if (m_state > RCON_CONNECTING) |
70 if (m_state > RCON_CONNECTING) |
71 { |
71 { |
72 // Say goodbye to remote |
72 // Say goodbye to remote |
73 Bytestream packet; |
73 Bytestream packet; |
74 packet.write_byte (CLRC_DISCONNECT); |
74 packet.write_byte(CLRC_DISCONNECT); |
75 this->send (packet); |
75 this->send(packet); |
76 m_interface->disconnected(); |
76 m_interface->disconnected(); |
77 } |
77 } |
78 |
78 |
79 m_state = RCON_DISCONNECTED; |
79 m_state = RCON_DISCONNECTED; |
80 } |
80 } |
81 |
81 |
82 // ------------------------------------------------------------------------------------------------- |
82 // ------------------------------------------------------------------------------------------------- |
83 // |
83 // |
84 void RCONSession::send (const Bytestream& packet) |
84 void RCONSession::send(const Bytestream& packet) |
85 { |
85 { |
86 m_socket.send (m_address, packet); |
86 m_socket.send(m_address, packet); |
87 } |
87 } |
88 |
88 |
89 // ------------------------------------------------------------------------------------------------- |
89 // ------------------------------------------------------------------------------------------------- |
90 // |
90 // |
91 void RCONSession::tick() |
91 void RCONSession::tick() |
92 { |
92 { |
93 if (m_state == RCON_DISCONNECTED) |
93 if (m_state == RCON_DISCONNECTED) |
94 return; |
94 return; |
95 |
95 |
96 time_t now; |
96 time_t now; |
97 time (&now); |
97 time(&now); |
98 |
98 |
99 if (m_lastPing < now) |
99 if (m_lastPing < now) |
100 { |
100 { |
101 if (m_state == RCON_CONNECTING) |
101 if (m_state == RCON_CONNECTING) |
102 { |
102 { |
103 send_hello(); |
103 sendHello(); |
104 } |
104 } |
105 else if (m_state == RCON_AUTHENTICATING) |
105 else if (m_state == RCON_AUTHENTICATING) |
106 { |
106 { |
107 send_password(); |
107 sendPassword(); |
108 } |
108 } |
109 else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) |
109 else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) |
110 { |
110 { |
111 Bytestream packet; |
111 Bytestream packet; |
112 packet.write_byte (CLRC_PONG); |
112 packet.write_byte(CLRC_PONG); |
113 send (packet); |
113 send(packet); |
114 bump_last_ping(); |
114 bumpLastPing(); |
115 } |
115 } |
116 } |
116 } |
117 |
117 |
118 for (Datagram datagram; m_socket.read (datagram);) |
118 for (Datagram datagram; m_socket.read(datagram);) |
119 handle_packet (datagram); |
119 handlePacket(datagram); |
120 } |
120 } |
121 |
121 |
122 // ------------------------------------------------------------------------------------------------- |
122 // ------------------------------------------------------------------------------------------------- |
123 // |
123 // |
124 void RCONSession::handle_packet (Datagram& datagram) |
124 void RCONSession::handlePacket(Datagram& datagram) |
125 { |
125 { |
126 if (datagram.address != m_address) |
126 if (datagram.address != m_address) |
127 return; |
127 return; |
128 |
128 |
129 try |
129 try |
130 { |
130 { |
131 while (datagram.message.bytes_left() > 0) |
131 while (datagram.message.bytes_left() > 0) |
132 { |
132 { |
133 int header = datagram.message.read_byte(); |
133 int header = datagram.message.read_byte(); |
134 |
134 |
135 switch (ServerResponse (header)) |
135 switch (ServerResponse(header)) |
136 { |
136 { |
137 case SVRC_OLDPROTOCOL: |
137 case SVRC_OLDPROTOCOL: |
138 m_interface->printError ("Your RCON client is using outdated protocol.\n"); |
138 m_interface->printError("Your RCON client is using outdated protocol.\n"); |
139 m_state = RCON_DISCONNECTED; |
139 m_state = RCON_DISCONNECTED; |
140 break; |
140 break; |
141 |
141 |
142 case SVRC_BANNED: |
142 case SVRC_BANNED: |
143 m_interface->printError ("You have been banned from the server.\n"); |
143 m_interface->printError("You have been banned from the server.\n"); |
144 m_state = RCON_DISCONNECTED; |
144 m_state = RCON_DISCONNECTED; |
145 break; |
145 break; |
146 |
146 |
147 case SVRC_SALT: |
147 case SVRC_SALT: |
148 m_salt = datagram.message.read_string(); |
148 m_salt = datagram.message.read_string(); |
149 m_state = RCON_AUTHENTICATING; |
149 m_state = RCON_AUTHENTICATING; |
150 send_password(); |
150 sendPassword(); |
151 break; |
151 break; |
152 |
152 |
153 case SVRC_INVALIDPASSWORD: |
153 case SVRC_INVALIDPASSWORD: |
154 m_interface->printError ("Login failed.\n"); |
154 m_interface->printError("Login failed.\n"); |
155 m_state = RCON_DISCONNECTED; |
155 m_state = RCON_DISCONNECTED; |
156 break; |
156 break; |
157 |
157 |
158 case SVRC_MESSAGE: |
158 case SVRC_MESSAGE: |
159 { |
159 { |
160 String message = datagram.message.read_string(); |
160 String message = datagram.message.read_string(); |
161 message.normalize(); |
161 message.normalize(); |
162 m_interface->printText ("%s\n", message.chars()); |
162 m_interface->printText("%s\n", message.chars()); |
163 } |
163 } |
164 break; |
164 break; |
165 |
165 |
166 case SVRC_LOGGEDIN: |
166 case SVRC_LOGGEDIN: |
167 m_interface->print ("Login successful!\n"); |
167 m_interface->print("Login successful!\n"); |
168 m_serverProtocol = datagram.message.read_byte(); |
168 m_serverProtocol = datagram.message.read_byte(); |
169 m_hostname = datagram.message.read_string(); |
169 m_hostname = datagram.message.read_string(); |
170 m_interface->setTitle (m_hostname); |
170 m_interface->setTitle(m_hostname); |
171 m_state = RCON_CONNECTED; |
171 m_state = RCON_CONNECTED; |
172 |
172 |
173 for (int i = datagram.message.read_byte(); i > 0; --i) |
173 for (int i = datagram.message.read_byte(); i > 0; --i) |
174 process_server_updates (datagram.message); |
174 processServerUpdates(datagram.message); |
175 |
175 |
176 m_interface->print ("Previous messages:\n"); |
176 m_interface->print("Previous messages:\n"); |
177 |
177 |
178 for (int i = datagram.message.read_byte(); i > 0; --i) |
178 for (int i = datagram.message.read_byte(); i > 0; --i) |
179 { |
179 { |
180 String message = datagram.message.read_string(); |
180 String message = datagram.message.read_string(); |
181 message.normalize(); |
181 message.normalize(); |
182 m_interface->printText ("--- %s\n", message.chars()); |
182 m_interface->printText("--- %s\n", message.chars()); |
183 } |
183 } |
184 |
184 |
185 m_interface->print ("End of previous messages.\n"); |
185 m_interface->print("End of previous messages.\n"); |
186 break; |
186 break; |
187 |
187 |
188 case SVRC_UPDATE: |
188 case SVRC_UPDATE: |
189 process_server_updates (datagram.message); |
189 processServerUpdates(datagram.message); |
190 break; |
190 break; |
191 |
191 |
192 case SVRC_TOOMANYTABCOMPLETES: |
192 case SVRC_TOOMANYTABCOMPLETES: |
193 { |
193 { |
194 unsigned int numCompletions = datagram.message.read_short(); |
194 unsigned int numCompletions = datagram.message.read_short(); |
195 m_interface->print ("%d completions for '%s'.\n", |
195 m_interface->print("%d completions for '%s'.\n", |
196 int (numCompletions), m_lastTabComplete.chars()); |
196 int(numCompletions), m_lastTabComplete.chars()); |
197 } |
197 } |
198 break; |
198 break; |
199 |
199 |
200 case SVRC_TABCOMPLETE: |
200 case SVRC_TABCOMPLETE: |
201 { |
201 { |
205 for (String& completion : completes) |
205 for (String& completion : completes) |
206 completion = datagram.message.read_string(); |
206 completion = datagram.message.read_string(); |
207 |
207 |
208 if (completes.size() == 1) |
208 if (completes.size() == 1) |
209 { |
209 { |
210 m_interface->tabComplete (m_lastTabComplete, completes[0]); |
210 m_interface->tabComplete(m_lastTabComplete, completes[0]); |
211 } |
211 } |
212 else if (not completes.is_empty()) |
212 else if (not completes.is_empty()) |
213 { |
213 { |
214 m_interface->print ("Completions for '%s':\n", m_lastTabComplete.chars()); |
214 m_interface->print("Completions for '%s':\n", m_lastTabComplete.chars()); |
215 |
215 |
216 for (int i : range(0, completes.size(), 8)) |
216 for (int i : range(0, completes.size(), 8)) |
217 { |
217 { |
218 Range<int> spliceRange (i, min (i + 8, completes.size())); |
218 Range<int> spliceRange(i, min(i + 8, completes.size())); |
219 StringList splice (completes.splice (spliceRange)); |
219 StringList splice(completes.splice(spliceRange)); |
220 m_interface->print ("- %s\n", splice.join (", ").chars()); |
220 m_interface->print("- %s\n", splice.join(", ").chars()); |
221 } |
221 } |
222 } |
222 } |
223 } |
223 } |
224 break; |
224 break; |
225 } |
225 } |
226 } |
226 } |
227 } |
227 } |
228 catch (std::exception& e) |
228 catch (std::exception& e) |
229 { |
229 { |
230 m_interface->printWarning ("Couldn't process packet: %s\n", e.what()); |
230 m_interface->printWarning("Couldn't process packet: %s\n", e.what()); |
231 } |
231 } |
232 } |
232 } |
233 |
233 |
234 void RCONSession::process_server_updates (Bytestream& packet) |
234 void RCONSession::processServerUpdates(Bytestream& packet) |
235 { |
235 { |
236 int header = packet.read_byte(); |
236 int header = packet.read_byte(); |
237 |
237 |
238 switch (RCONUpdateType (header)) |
238 switch (RCONUpdateType(header)) |
239 { |
239 { |
240 case SVRCU_PLAYERDATA: |
240 case SVRCU_PLAYERDATA: |
241 { |
241 { |
242 StringList players; |
242 StringList players; |
243 |
243 |
244 for (int i = packet.read_byte(); i > 0; --i) |
244 for (int i = packet.read_byte(); i > 0; --i) |
245 players.append (packet.read_string()); |
245 players.append(packet.read_string()); |
246 |
246 |
247 m_interface->setPlayerNames (players); |
247 m_interface->setPlayerNames(players); |
248 } |
248 } |
249 break; |
249 break; |
250 |
250 |
251 case SVRCU_ADMINCOUNT: |
251 case SVRCU_ADMINCOUNT: |
252 m_numAdmins = packet.read_byte(); |
252 m_adminCount = packet.read_byte(); |
253 m_interface->updateStatusBar(); |
253 m_interface->updateStatusBar(); |
254 break; |
254 break; |
255 |
255 |
256 case SVRCU_MAP: |
256 case SVRCU_MAP: |
257 m_level = packet.read_string(); |
257 m_level = packet.read_string(); |
258 m_interface->updateStatusBar(); |
258 m_interface->updateStatusBar(); |
259 break; |
259 break; |
260 |
260 |
261 default: |
261 default: |
262 m_interface->printWarning ("Unknown server update type: %d\n", header); |
262 m_interface->printWarning("Unknown server update type: %d\n", header); |
263 break; |
263 break; |
264 } |
264 } |
265 } |
265 } |
266 |
266 |
267 // ------------------------------------------------------------------------------------------------- |
267 // ------------------------------------------------------------------------------------------------- |
268 // |
268 // |
269 UDPSocket* RCONSession::socket() |
269 UDPSocket* RCONSession::getSocket() |
270 { |
270 { |
271 return &m_socket; |
271 return &m_socket; |
272 } |
272 } |
273 |
273 |
274 // ------------------------------------------------------------------------------------------------- |
274 // ------------------------------------------------------------------------------------------------- |
275 // |
275 // |
276 void RCONSession::send_hello() |
276 void RCONSession::sendHello() |
277 { |
277 { |
278 m_interface->print ("Connecting to %s...\n", |
278 m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).chars()); |
279 m_address.to_string (IPAddress::WITH_PORT).chars()); |
|
280 Bytestream packet; |
279 Bytestream packet; |
281 packet.write_byte (CLRC_BEGINCONNECTION); |
280 packet.write_byte(CLRC_BEGINCONNECTION); |
282 packet.write_byte (RCON_PROTOCOL_VERSION); |
281 packet.write_byte(RCON_PROTOCOL_VERSION); |
283 send (packet); |
282 send(packet); |
284 bump_last_ping(); |
283 bumpLastPing(); |
285 } |
284 } |
286 |
285 |
287 // ------------------------------------------------------------------------------------------------- |
286 // ------------------------------------------------------------------------------------------------- |
288 // |
287 // |
289 void RCONSession::send_password() |
288 void RCONSession::sendPassword() |
290 { |
289 { |
291 m_interface->print ("Authenticating...\n"); |
290 m_interface->print("Authenticating...\n"); |
292 Bytestream packet; |
291 Bytestream packet; |
293 packet.write_byte (CLRC_PASSWORD); |
292 packet.write_byte(CLRC_PASSWORD); |
294 packet.write_string ((m_salt + m_password).md5()); |
293 packet.write_string((m_salt + m_password).md5()); |
295 send (packet); |
294 send(packet); |
296 bump_last_ping(); |
295 bumpLastPing(); |
297 } |
296 } |
298 |
297 |
299 // ------------------------------------------------------------------------------------------------- |
298 // ------------------------------------------------------------------------------------------------- |
300 // |
299 // |
301 void RCONSession::set_password (const String& password) |
300 void RCONSession::setPassword(const String& password) |
302 { |
301 { |
303 m_password = password; |
302 m_password = password; |
304 } |
303 } |
305 |
304 |
306 // ------------------------------------------------------------------------------------------------- |
305 // ------------------------------------------------------------------------------------------------- |
307 // |
306 // |
308 void RCONSession::bump_last_ping() |
307 void RCONSession::bumpLastPing() |
309 { |
308 { |
310 time_t now; |
309 time_t now; |
311 time (&now); |
310 time(&now); |
312 m_lastPing = now; |
311 m_lastPing = now; |
313 } |
312 } |
314 |
313 |
315 // ------------------------------------------------------------------------------------------------- |
314 // ------------------------------------------------------------------------------------------------- |
316 // |
315 // |
317 bool RCONSession::is_active() const |
316 bool RCONSession::isActive() const |
318 { |
317 { |
319 return state() != RCON_DISCONNECTED; |
318 return getState() != RCON_DISCONNECTED; |
320 } |
319 } |
321 |
320 |
322 // ------------------------------------------------------------------------------------------------- |
321 // ------------------------------------------------------------------------------------------------- |
323 // Returns true if the message was successfully sent. |
322 // Returns true if the message was successfully sent. |
324 // |
323 // |
325 bool RCONSession::send_command (const String& message) |
324 bool RCONSession::sendCommand(const String& message) |
326 { |
325 { |
327 if (m_state != RCON_CONNECTED or message.isEmpty()) |
326 if (m_state != RCON_CONNECTED or message.isEmpty()) |
328 return false; |
327 return false; |
329 |
328 |
330 Bytestream packet; |
329 Bytestream packet; |
331 packet.write_byte (CLRC_COMMAND); |
330 packet.write_byte(CLRC_COMMAND); |
332 packet.write_string (message); |
331 packet.write_string(message); |
333 send (packet); |
332 send(packet); |
334 bump_last_ping(); |
333 bumpLastPing(); |
335 return true; |
334 return true; |
336 } |
335 } |
337 |
336 |
338 // ------------------------------------------------------------------------------------------------- |
337 // ------------------------------------------------------------------------------------------------- |
339 // |
338 // |
340 RCONSessionState RCONSession::state() const |
339 RCONSessionState RCONSession::getState() const |
341 { |
340 { |
342 return m_state; |
341 return m_state; |
343 } |
342 } |
344 |
343 |
345 // ------------------------------------------------------------------------------------------------- |
344 // ------------------------------------------------------------------------------------------------- |
349 return m_address; |
348 return m_address; |
350 } |
349 } |
351 |
350 |
352 // ------------------------------------------------------------------------------------------------- |
351 // ------------------------------------------------------------------------------------------------- |
353 // |
352 // |
354 int RCONSession::num_admins() const |
353 int RCONSession::getAdminCount() const |
355 { |
354 { |
356 return m_numAdmins; |
355 return m_adminCount; |
357 } |
356 } |
358 |
357 |
359 // ------------------------------------------------------------------------------------------------- |
358 // ------------------------------------------------------------------------------------------------- |
360 // |
359 // |
361 const String& RCONSession::level() const |
360 const String& RCONSession::getLevel() const |
362 { |
361 { |
363 return m_level; |
362 return m_level; |
364 } |
363 } |
365 |
364 |
366 // ------------------------------------------------------------------------------------------------- |
365 // ------------------------------------------------------------------------------------------------- |
367 // |
366 // |
368 void RCONSession::request_tab_complete (const String& part) |
367 void RCONSession::requestTabCompletion(const String& part) |
369 { |
368 { |
370 if (m_serverProtocol >= 4) |
369 if (m_serverProtocol >= 4) |
371 { |
370 { |
372 Bytestream packet; |
371 Bytestream packet; |
373 packet.write_byte (CLRC_TABCOMPLETE); |
372 packet.write_byte(CLRC_TABCOMPLETE); |
374 packet.write_string (part); |
373 packet.write_string(part); |
375 send (packet); |
374 send(packet); |
376 bump_last_ping(); |
375 bumpLastPing(); |
377 m_lastTabComplete = part; |
376 m_lastTabComplete = part; |
378 } |
377 } |
379 else |
378 else |
380 { |
379 { |
381 m_interface->print ("This server does not support tab-completion\n", m_serverProtocol); |
380 m_interface->print("This server does not support tab-completion\n", m_serverProtocol); |
382 } |
381 } |
383 } |
382 } |
384 |
383 |
385 // ------------------------------------------------------------------------------------------------- |
384 // ------------------------------------------------------------------------------------------------- |
386 // |
385 // |
387 void RCONSession::set_interface (Interface* iface) |
386 void RCONSession::setInterface(Interface* interface) |
388 { |
387 { |
389 m_interface = iface; |
388 m_interface = interface; |
390 } |
389 } |
391 |
390 |
392 END_ZFC_NAMESPACE |
391 END_ZFC_NAMESPACE |