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 send({CLRC_DISCONNECT}); |
74 packet.write_byte (CLRC_DISCONNECT); |
|
75 this->send (packet); |
|
76 m_interface->disconnected(); |
74 m_interface->disconnected(); |
77 } |
75 } |
78 |
76 |
79 m_state = RCON_DISCONNECTED; |
77 m_state = RCON_DISCONNECTED; |
80 } |
78 } |
81 |
79 |
82 // ------------------------------------------------------------------------------------------------- |
80 // ------------------------------------------------------------------------------------------------- |
83 // |
81 // |
84 void RCONSession::send (const Bytestream& packet) |
82 void RCONSession::send(const ByteArray& packet) |
85 { |
83 { |
86 m_socket.send (m_address, packet); |
84 m_socket.send(m_address, packet); |
87 } |
85 } |
88 |
86 |
89 // ------------------------------------------------------------------------------------------------- |
87 // ------------------------------------------------------------------------------------------------- |
90 // |
88 // |
91 void RCONSession::tick() |
89 void RCONSession::tick() |
92 { |
90 { |
93 if (m_state == RCON_DISCONNECTED) |
91 if (m_state == RCON_DISCONNECTED) |
94 return; |
92 return; |
95 |
93 |
96 time_t now; |
94 time_t now; |
97 time (&now); |
95 time(&now); |
98 |
96 |
99 if (m_lastPing < now) |
97 if (m_lastPing < now) |
100 { |
98 { |
101 if (m_state == RCON_CONNECTING) |
99 if (m_state == RCON_CONNECTING) |
102 { |
100 { |
103 send_hello(); |
101 sendHello(); |
104 } |
102 } |
105 else if (m_state == RCON_AUTHENTICATING) |
103 else if (m_state == RCON_AUTHENTICATING) |
106 { |
104 { |
107 send_password(); |
105 sendPassword(); |
108 } |
106 } |
109 else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) |
107 else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) |
110 { |
108 { |
111 Bytestream packet; |
109 send({CLRC_PONG}); |
112 packet.write_byte (CLRC_PONG); |
110 bumpLastPing(); |
113 send (packet); |
|
114 bump_last_ping(); |
|
115 } |
111 } |
116 } |
112 } |
117 |
113 |
118 for (Datagram datagram; m_socket.read (datagram);) |
114 for (Datagram datagram; m_socket.read(datagram);) |
119 handle_packet (datagram); |
115 handlePacket(datagram); |
120 } |
116 } |
121 |
117 |
122 // ------------------------------------------------------------------------------------------------- |
118 // ------------------------------------------------------------------------------------------------- |
123 // |
119 // |
124 void RCONSession::handle_packet (Datagram& datagram) |
120 void RCONSession::handlePacket(Datagram& datagram) |
125 { |
121 { |
126 if (datagram.address != m_address) |
122 if (datagram.address != m_address) |
127 return; |
123 return; |
|
124 |
|
125 Bytestream stream(datagram.message); |
128 |
126 |
129 try |
127 try |
130 { |
128 { |
131 int32_t header = datagram.message.read_long(); |
129 int32_t header = datagram.message.read_long(); |
132 int32_t sequenceNumber = (header != 0) ? datagram.message.read_long() : 0; |
130 int32_t sequenceNumber = (header != 0) ? datagram.message.read_long() : 0; |
133 m_interface->print("Recieved packet with header 0x%x and sequence number #%d\n", header, sequenceNumber); |
131 m_interface->print("Recieved packet with header 0x%x and sequence number #%d\n", header, sequenceNumber); |
134 |
132 |
135 while (datagram.message.bytes_left() > 0) |
133 while (datagram.message.bytes_left() > 0) |
136 { |
134 { |
137 int header = datagram.message.read_byte(); |
135 int header = stream.readByte(); |
138 |
136 |
139 switch (ServerResponse (header)) |
137 switch (ServerResponse(header)) |
140 { |
138 { |
141 case SVRC_OLDPROTOCOL: |
139 case SVRC_OLDPROTOCOL: |
142 m_interface->printError ("Your RCON client is using outdated protocol.\n"); |
140 m_interface->printError("Your RCON client is using outdated protocol.\n"); |
143 m_state = RCON_DISCONNECTED; |
141 m_state = RCON_DISCONNECTED; |
144 break; |
142 break; |
145 |
143 |
146 case SVRC_BANNED: |
144 case SVRC_BANNED: |
147 m_interface->printError ("You have been banned from the server.\n"); |
145 m_interface->printError("You have been banned from the server.\n"); |
148 m_state = RCON_DISCONNECTED; |
146 m_state = RCON_DISCONNECTED; |
149 break; |
147 break; |
150 |
148 |
151 case SVRC_SALT: |
149 case SVRC_SALT: |
152 m_salt = datagram.message.read_string(); |
150 m_salt = stream.readString(); |
153 m_state = RCON_AUTHENTICATING; |
151 m_state = RCON_AUTHENTICATING; |
154 send_password(); |
152 sendPassword(); |
155 break; |
153 break; |
156 |
154 |
157 case SVRC_INVALIDPASSWORD: |
155 case SVRC_INVALIDPASSWORD: |
158 m_interface->printError ("Login failed.\n"); |
156 m_interface->printError("Login failed.\n"); |
159 m_state = RCON_DISCONNECTED; |
157 m_state = RCON_DISCONNECTED; |
160 break; |
158 break; |
161 |
159 |
162 case SVRC_MESSAGE: |
160 case SVRC_MESSAGE: |
163 { |
161 { |
164 String message = datagram.message.read_string(); |
162 String message = stream.readString(); |
165 message.normalize(); |
163 message.normalize(); |
166 m_interface->printText ("%s\n", message.chars()); |
164 m_interface->printText("%s\n", message.chars()); |
167 } |
165 } |
168 break; |
166 break; |
169 |
167 |
170 case SVRC_LOGGEDIN: |
168 case SVRC_LOGGEDIN: |
171 m_interface->print ("Login successful!\n"); |
169 m_interface->print("Login successful!\n"); |
172 m_serverProtocol = datagram.message.read_byte(); |
170 m_serverProtocol = stream.readByte(); |
173 m_hostname = datagram.message.read_string(); |
171 m_hostname = stream.readString(); |
174 m_interface->setTitle (m_hostname); |
172 m_interface->setTitle(m_hostname); |
175 m_state = RCON_CONNECTED; |
173 m_state = RCON_CONNECTED; |
176 |
174 |
177 for (int i = datagram.message.read_byte(); i > 0; --i) |
175 for (int i = stream.readByte(); i > 0; --i) |
178 process_server_updates (datagram.message); |
176 processServerUpdates(stream); |
179 |
177 |
180 m_interface->print ("Previous messages:\n"); |
178 m_interface->print("Previous messages:\n"); |
181 |
179 |
182 for (int i = datagram.message.read_byte(); i > 0; --i) |
180 for (int i = stream.readByte(); i > 0; --i) |
183 { |
181 { |
184 String message = datagram.message.read_string(); |
182 String message = stream.readString(); |
185 message.normalize(); |
183 message.normalize(); |
186 m_interface->printText ("--- %s\n", message.chars()); |
184 m_interface->printText("--- %s\n", message.chars()); |
187 } |
185 } |
188 |
186 |
189 m_interface->print ("End of previous messages.\n"); |
187 m_interface->print("End of previous messages.\n"); |
190 |
188 |
191 // Watch sv_hostname so that we can update the titlebar when it changes. |
189 // Watch sv_hostname so that we can update the titlebar when it changes. |
192 request_watch("sv_hostname"); |
190 request_watch("sv_hostname"); |
193 m_interface->print ("Watch requested.\n"); |
191 m_interface->print ("Watch requested.\n"); |
194 break; |
192 break; |
195 |
193 |
196 case SVRC_UPDATE: |
194 case SVRC_UPDATE: |
197 process_server_updates (datagram.message); |
195 processServerUpdates(stream); |
198 break; |
196 break; |
199 |
197 |
200 case SVRC_TOOMANYTABCOMPLETES: |
198 case SVRC_TOOMANYTABCOMPLETES: |
201 { |
199 { |
202 unsigned int numCompletions = datagram.message.read_short(); |
200 unsigned int numCompletions = stream.readShort(); |
203 m_interface->print ("%d completions for '%s'.\n", |
201 m_interface->print("%d completions for '%s'.\n", |
204 int (numCompletions), m_lastTabComplete.chars()); |
202 int(numCompletions), m_lastTabComplete.chars()); |
205 } |
203 } |
206 break; |
204 break; |
207 |
205 |
208 case SVRC_TABCOMPLETE: |
206 case SVRC_TABCOMPLETE: |
209 { |
207 { |
210 StringList completes; |
208 StringList completes; |
211 completes.resize(datagram.message.read_byte()); |
209 completes.resize(stream.readByte()); |
212 |
210 |
213 for (String& completion : completes) |
211 for (String& completion : completes) |
214 completion = datagram.message.read_string(); |
212 completion = stream.readString(); |
215 |
213 |
216 if (completes.size() == 1) |
214 if (completes.size() == 1) |
217 { |
215 { |
218 m_interface->tabComplete (m_lastTabComplete, completes[0]); |
216 m_interface->tabComplete(m_lastTabComplete, completes[0]); |
219 } |
217 } |
220 else if (not completes.is_empty()) |
218 else if (not completes.is_empty()) |
221 { |
219 { |
222 m_interface->print ("Completions for '%s':\n", m_lastTabComplete.chars()); |
220 m_interface->print("Completions for '%s':\n", m_lastTabComplete.chars()); |
223 |
221 |
224 for (int i : range(0, completes.size(), 8)) |
222 for (int i : range(0, completes.size(), 8)) |
225 { |
223 { |
226 Range<int> spliceRange (i, min (i + 8, completes.size())); |
224 Range<int> spliceRange(i, min(i + 8, completes.size())); |
227 StringList splice (completes.splice (spliceRange)); |
225 StringList splice(completes.splice(spliceRange)); |
228 m_interface->print ("- %s\n", splice.join (", ").chars()); |
226 m_interface->print("- %s\n", splice.join(", ").chars()); |
229 } |
227 } |
230 } |
228 } |
231 } |
229 } |
232 break; |
230 break; |
233 |
231 |
267 } |
265 } |
268 } |
266 } |
269 } |
267 } |
270 catch (std::exception& e) |
268 catch (std::exception& e) |
271 { |
269 { |
272 m_interface->printWarning ("Couldn't process packet: %s\n", e.what()); |
270 m_interface->printWarning("Couldn't process packet: %s\n", e.what()); |
273 } |
271 } |
274 } |
272 } |
275 |
273 |
276 void RCONSession::process_server_updates (Bytestream& packet) |
274 void RCONSession::processServerUpdates(Bytestream& packet) |
277 { |
275 { |
278 int header = packet.read_byte(); |
276 int header = packet.readByte(); |
279 |
277 |
280 switch (RCONUpdateType (header)) |
278 switch (RCONUpdateType(header)) |
281 { |
279 { |
282 case SVRCU_PLAYERDATA: |
280 case SVRCU_PLAYERDATA: |
283 { |
281 { |
284 StringList players; |
282 StringList players; |
285 |
283 |
286 for (int i = packet.read_byte(); i > 0; --i) |
284 for (int i = packet.readByte(); i > 0; --i) |
287 players.append (packet.read_string()); |
285 players.append(packet.readString()); |
288 |
286 |
289 m_interface->setPlayerNames (players); |
287 m_interface->setPlayerNames(players); |
290 } |
288 } |
291 break; |
289 break; |
292 |
290 |
293 case SVRCU_ADMINCOUNT: |
291 case SVRCU_ADMINCOUNT: |
294 m_numAdmins = packet.read_byte(); |
292 m_adminCount = packet.readByte(); |
295 m_interface->updateStatusBar(); |
293 m_interface->updateStatusBar(); |
296 break; |
294 break; |
297 |
295 |
298 case SVRCU_MAP: |
296 case SVRCU_MAP: |
299 m_level = packet.read_string(); |
297 m_level = packet.readString(); |
300 m_interface->updateStatusBar(); |
298 m_interface->updateStatusBar(); |
301 break; |
299 break; |
302 |
300 |
303 default: |
301 default: |
304 m_interface->printWarning ("Unknown server update type: %d\n", header); |
302 m_interface->printWarning("Unknown server update type: %d\n", header); |
305 break; |
303 break; |
306 } |
304 } |
307 } |
305 } |
308 |
306 |
309 // ------------------------------------------------------------------------------------------------- |
307 // ------------------------------------------------------------------------------------------------- |
310 // |
308 // |
311 UDPSocket* RCONSession::socket() |
309 UDPSocket* RCONSession::getSocket() |
312 { |
310 { |
313 return &m_socket; |
311 return &m_socket; |
314 } |
312 } |
315 |
313 |
316 // ------------------------------------------------------------------------------------------------- |
314 // ------------------------------------------------------------------------------------------------- |
317 // |
315 // |
318 void RCONSession::send_hello() |
316 void RCONSession::sendHello() |
319 { |
317 { |
320 m_interface->print ("Connecting to %s...\n", |
318 m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).chars()); |
321 m_address.to_string (IPAddress::WITH_PORT).chars()); |
319 send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION}); |
322 Bytestream packet; |
320 bumpLastPing(); |
323 packet.write_byte (CLRC_BEGINCONNECTION); |
321 } |
324 packet.write_byte (RCON_PROTOCOL_VERSION); |
322 |
325 send (packet); |
323 // ------------------------------------------------------------------------------------------------- |
326 bump_last_ping(); |
324 // |
327 } |
325 void RCONSession::sendPassword() |
328 |
326 { |
329 // ------------------------------------------------------------------------------------------------- |
327 m_interface->print("Authenticating...\n"); |
330 // |
328 ByteArray message; |
331 void RCONSession::send_password() |
329 Bytestream stream(message); |
332 { |
330 stream.writeByte(CLRC_PASSWORD); |
333 m_interface->print ("Authenticating...\n"); |
331 stream.writeString((m_salt + m_password).md5()); |
334 Bytestream packet; |
332 send(message); |
335 packet.write_byte (CLRC_PASSWORD); |
333 bumpLastPing(); |
336 packet.write_string ((m_salt + m_password).md5()); |
334 } |
337 send (packet); |
335 |
338 bump_last_ping(); |
336 // ------------------------------------------------------------------------------------------------- |
339 } |
337 // |
340 |
338 void RCONSession::setPassword(const String& password) |
341 // ------------------------------------------------------------------------------------------------- |
|
342 // |
|
343 void RCONSession::set_password (const String& password) |
|
344 { |
339 { |
345 m_password = password; |
340 m_password = password; |
346 } |
341 } |
347 |
342 |
348 // ------------------------------------------------------------------------------------------------- |
343 // ------------------------------------------------------------------------------------------------- |
349 // |
344 // |
350 void RCONSession::bump_last_ping() |
345 void RCONSession::bumpLastPing() |
351 { |
346 { |
352 time_t now; |
347 time_t now; |
353 time (&now); |
348 time(&now); |
354 m_lastPing = now; |
349 m_lastPing = now; |
355 } |
350 } |
356 |
351 |
357 // ------------------------------------------------------------------------------------------------- |
352 // ------------------------------------------------------------------------------------------------- |
358 // |
353 // |
359 bool RCONSession::is_active() const |
354 bool RCONSession::isActive() const |
360 { |
355 { |
361 return state() != RCON_DISCONNECTED; |
356 return getState() != RCON_DISCONNECTED; |
362 } |
357 } |
363 |
358 |
364 // ------------------------------------------------------------------------------------------------- |
359 // ------------------------------------------------------------------------------------------------- |
365 // Returns true if the message was successfully sent. |
360 // Returns true if the message was successfully sent. |
366 // |
361 // |
367 bool RCONSession::send_command (const String& message) |
362 bool RCONSession::sendCommand(const String& commandString) |
368 { |
363 { |
369 if (m_state != RCON_CONNECTED or message.isEmpty()) |
364 if (m_state != RCON_CONNECTED or commandString.isEmpty()) |
370 return false; |
365 return false; |
371 |
366 |
372 Bytestream packet; |
367 ByteArray message; |
373 packet.write_byte (CLRC_COMMAND); |
368 Bytestream stream(message); |
374 packet.write_string (message); |
369 stream.writeByte(CLRC_COMMAND); |
375 send (packet); |
370 stream.writeString(commandString); |
376 bump_last_ping(); |
371 send(message); |
|
372 bumpLastPing(); |
377 return true; |
373 return true; |
378 } |
374 } |
379 |
375 |
380 // ------------------------------------------------------------------------------------------------- |
376 // ------------------------------------------------------------------------------------------------- |
381 // |
377 // |
382 RCONSessionState RCONSession::state() const |
378 RCONSessionState RCONSession::getState() const |
383 { |
379 { |
384 return m_state; |
380 return m_state; |
385 } |
381 } |
386 |
382 |
387 // ------------------------------------------------------------------------------------------------- |
383 // ------------------------------------------------------------------------------------------------- |