1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie, Albert Zeyer and Martin Griffin
7 //
8 //
9 /////////////////////////////////////////
10
11
12 // Server class - Parsing
13 // Created 1/7/02
14 // Jason Boettcher
15
16
17
18 #include "LieroX.h"
19 #include "FindFile.h"
20 #include "CServer.h"
21 #include "ProfileSystem.h"
22 #include "DeprecatedGUI/Menu.h"
23 #include "CServerConnection.h"
24 #include "CServerNetEngine.h"
25 #include "CChannel.h"
26 #include "StringUtils.h"
27 #include "CWorm.h"
28 #include "Protocol.h"
29 #include "ConfigHandler.h"
30 #include "ChatCommand.h"
31 #include "DedicatedControl.h"
32 #include "AuxLib.h"
33 #include "Version.h"
34 #include "Timer.h"
35 #include "NotifyUser.h"
36 #include "XMLutils.h"
37 #include "CClientNetEngine.h"
38 #include "IpToCountryDB.h"
39 #include "Debug.h"
40 #include "CGameMode.h"
41 #include "FlagInfo.h"
42 #include "WeaponDesc.h"
43 #include "Autocompletion.h"
44 #include "Command.h"
45 #include "TaskManager.h"
46
47
48 #ifdef _MSC_VER
49 #undef min
50 #undef max
51 #endif
52
53
54 // TODO: move this out here after we have moved all the Send/Parse stuff
55 // declare them only locally here as nobody really should use them explicitly
56 std::string OldLxCompatibleString(const std::string &Utf8String);
57 std::string Utf8String(const std::string &OldLxString);
58
59
getConnectionArrayIndex()60 int CServerNetEngine::getConnectionArrayIndex() {
61 if(!cl) {
62 errors << "CServerNetEngine::getConnectionArrayIndex: connection not set" << endl;
63 return -1;
64 }
65
66 if(cl->getNetEngine() != this) {
67 errors << "CServerNetEngine::getConnectionArrayIndex: net engine not consistent" << endl;
68 return -1;
69 }
70
71 return cl->getConnectionArrayIndex();
72 }
73
74
75 /*
76 =======================================
77 Connected Packets
78 =======================================
79 */
80
81 ///////////////////
82 // Parse a packet
ParsePacket(CBytestream * bs)83 void CServerNetEngine::ParsePacket(CBytestream *bs) {
84
85 // TODO: That's hack, all processing should be done in CChannel_056b
86 // Maybe server will work without that code at all, should check against 0.56b
87 CChannel *chan = cl->getChannel();
88 if( chan != NULL )
89 {
90 chan->recheckSeqs();
91 }
92
93 cl->setLastReceived(tLX->currentTime);
94
95
96 // Do not process empty packets
97 if (bs->isPosAtEnd())
98 return;
99
100 #if defined(DEBUG) || !defined(FUZZY_ERROR_TESTING_C2S)
101 typedef std::list<CBytestream> BSList;
102 BSList bss;
103 #endif
104
105 uchar cmd;
106
107 while (!bs->isPosAtEnd()) {
108 #ifdef DEBUG
109 size_t startPos = bs->GetPos();
110 #endif
111 cmd = bs->readInt(1);
112
113 switch (cmd) {
114
115 // Client is ready
116 case C2S_IMREADY:
117 ParseImReady(bs);
118 break;
119
120 // Update packet
121 case C2S_UPDATE:
122 ParseUpdate(bs);
123 break;
124
125 // Death
126 case C2S_DEATH:
127 ParseDeathPacket(bs);
128 break;
129
130 // Chat text
131 case C2S_CHATTEXT:
132 ParseChatText(bs);
133 break;
134
135 case C2S_CHATCMDCOMPLREQ:
136 ParseChatCommandCompletionRequest(bs);
137 break;
138
139 case C2S_AFK:
140 ParseAFK(bs);
141 break;
142
143 // Update lobby
144 case C2S_UPDATELOBBY:
145 ParseUpdateLobby(bs);
146 break;
147
148 // Disconnect
149 case C2S_DISCONNECT:
150 ParseDisconnect();
151 break;
152
153 // Bonus grabbed
154 case C2S_GRABBONUS:
155 ParseGrabBonus(bs);
156 break;
157
158 case C2S_SENDFILE:
159 ParseSendFile(bs);
160 break;
161
162 case C2S_REPORTDAMAGE:
163 ParseReportDamage(bs);
164 break;
165
166 default:
167 // HACK, HACK: old olx/lxp clients send the ping twice, once normally once per channel
168 // which leads to warnings here - we simply parse it here and avoid warnings
169
170 /*
171 It's a bug in LieroX Pro that sent the ping
172 packet twice: once per CChannel - it looked like this:
173 (8 bytes of CChannel's sequence)(Connectionless header)(Packet itself)
174 and then the same packet normally (via CBytestream::Send)
175 (Connectionless header) (Packet itself)
176
177 The hack solved the first case which generated warning of unknown packet.
178 */
179
180 // Avoid "reading from stream behind end" warning if this is really a bad packet
181 // and print the bad command instead
182 if (cmd == 0xff && bs->GetRestLen() > 3) {
183 if (bs->readInt(3) == 0xffffff) {
184 std::string address;
185 NetAddrToString(cl->getChannel()->getAddress(), address);
186 server->ParseConnectionlessPacket(cl->getChannel()->getSocket(), bs, address);
187 break;
188 }
189 bs->revertByte(); bs->revertByte(); bs->revertByte();
190 }
191
192 // Really a bad packet
193 #if !defined(FUZZY_ERROR_TESTING_C2S)
194 warnings << "sv: Bad command in packet (" << itoa(cmd) << ") from " << cl->debugName() << endl;
195 if(cl->isLocalClient()) {
196 notes << "Bad package from local client" << endl;
197 for(BSList::iterator i = bss.begin(); i != bss.end(); ++i) {
198 notes << "already parsed packet:" << endl;
199 i->Dump();
200 }
201 notes << "full bytestream:" << endl;
202 bs->revertByte(); // to have the cmd-byte the marked byte in the dump
203 bs->Dump();
204 bs->readByte(); // skip the bad cmd-byte
205 }
206 #endif
207
208 // The stream is screwed up and we should ignore the rest.
209 // In most cases, we would get further bad command errors but
210 // sometimes we would parse wrongly some invalid stuff.
211 bs->SkipAll();
212 }
213
214 #ifdef DEBUG
215 if(!bs->isPosAtEnd())
216 bss.push_back( CBytestream( bs->getRawData(startPos, bs->GetPos()) ) );
217 #endif
218 }
219 }
220
221
222 ///////////////////
223 // Parse a 'im ready' packet
ParseImReady(CBytestream * bs)224 void CServerNetEngine::ParseImReady(CBytestream *bs) {
225 if ( server->iState == SVS_LOBBY )
226 {
227 notes << "GameServer::ParseImReady: Not waiting for ready, packet is being ignored." << endl;
228
229 // Skip to get the correct position in the stream
230 int num = bs->readByte();
231 for (int i = 0;i < num;i++) {
232 bs->Skip(1);
233 CWorm::skipWeapons(bs);
234 }
235
236 return;
237 }
238
239 int i, j;
240 // Note: This isn't a lobby ready
241
242 // Read the worms weapons
243 int num = bs->readByte();
244 if(server->serverChoosesWeapons() && num > 0) {
245 // It's only a note because <Beta9 clients will do that wrong anyway.
246 notes << "ParseImReady: " << cl->debugName() << " wants to set own weapons but we have serverChoosesWeapons" << endl;
247 }
248 for (i = 0; i < num; i++) {
249 if(bs->isPosAtEnd()) {
250 warnings << "ParseImReady: packaged screwed" << endl;
251 break;
252 }
253 int id = bs->readByte();
254 if (id >= 0 && id < MAX_WORMS) {
255 if(!server->cWorms[id].isUsed()) {
256 warnings << "ParseImReady: got unused worm-ID!" << endl;
257 CWorm::skipWeapons(bs);
258 continue;
259 }
260 if(!cl->OwnsWorm(id)) {
261 warnings << "ParseImReady: " << cl->debugName() << " wants to update the weapons of worm " << id << endl;
262 CWorm::skipWeapons(bs);
263 continue;
264 }
265 if(server->serverChoosesWeapons()) {
266 // we already made a warning about that
267 CWorm::skipWeapons(bs);
268 continue;
269 }
270 notes << "Server:ParseImReady: ";
271 server->cWorms[id].readWeapons(bs);
272 for (j = 0; j < 5; j++) {
273 if(server->cWorms[id].getWeapon(j)->Weapon)
274 server->cWorms[id].getWeapon(j)->Enabled =
275 server->cWeaponRestrictions.isEnabled(server->cWorms[id].getWeapon(j)->Weapon->Name) ||
276 server->cWeaponRestrictions.isBonus(server->cWorms[id].getWeapon(j)->Weapon->Name);
277 }
278
279 } else { // wrong id -> Skip to get the right position
280 warnings << "ParseImReady: got wrong worm-ID!" << endl;
281 CWorm::skipWeapons(bs);
282 }
283 }
284
285
286 // Set this client to 'ready'
287 notes << "Server: client " << cl->debugName() << " got ready" << endl;
288 cl->setGameReady(true);
289
290 SendClientReady(NULL);
291
292 if(server->iState == SVS_PLAYING)
293 server->BeginMatch(cl);
294 else if(tLXOptions->tGameInfo.features[FT_ImmediateStart])
295 server->BeginMatch(cl);
296 else
297 // Check if all the clients are ready
298 server->CheckReadyClient();
299 }
300
301
302 ///////////////////
303 // Parse an update packet
ParseUpdate(CBytestream * bs)304 void CServerNetEngine::ParseUpdate(CBytestream *bs) {
305 for (short i = 0; i < cl->getNumWorms(); i++) {
306 CWorm *w = cl->getWorm(i);
307
308 bool wasShootingBefore = w->getWormState()->bShoot;
309 const weapon_t* oldWeapon = (w->getCurWeapon() && w->getCurWeapon()->Enabled) ? w->getCurWeapon()->Weapon : NULL;
310 w->readPacket(bs, server->cWorms);
311
312 if(server->iState == SVS_PLAYING) {
313 // If the worm is shooting, handle it
314 if (w->getWormState()->bShoot && w->getAlive())
315 server->WormShoot(w); // handle shot and add to shootlist to send it later to the clients
316
317 // handle FinalProj for weapon
318 if(oldWeapon && ((wasShootingBefore && !w->getWormState()->bShoot) || (wasShootingBefore && oldWeapon != w->getCurWeapon()->Weapon)))
319 server->WormShootEnd(w, oldWeapon);
320 }
321 }
322 }
323
324
325 ///////////////////
326 // Parse a death packet
ParseDeathPacket(CBytestream * bs)327 void CServerNetEngine::ParseDeathPacket(CBytestream *bs) {
328 // No kills in lobby
329 if (server->iState != SVS_PLAYING) {
330 notes << "GameServer::ParseDeathPacket: Not playing, ignoring the packet." << endl;
331
332 // Skip to get the correct position in the stream
333 bs->Skip(2);
334
335 return;
336 }
337
338 int victim = bs->readInt(1);
339 int killer = bs->readInt(1);
340
341 // Bad packet
342 if (bs->GetPos() > bs->GetLength()) {
343 warnings << "GameServer::ParseDeathPacket: Reading beyond the end of stream." << endl;
344 return;
345 }
346
347 // If the game is already over, ignore this
348 if (server->bGameOver) {
349 notes("GameServer::killWorm: Game is over, ignoring.\n");
350 return;
351 }
352 // Safety check
353 if (victim < 0 || victim >= MAX_WORMS) {
354 warnings("GameServer::killWorm: victim ID out of bounds.\n");
355 return;
356 }
357 if (killer < 0 || killer >= MAX_WORMS) {
358 warnings("GameServer::killWorm: killer ID out of bounds.\n");
359 return;
360 }
361
362 if (tLXOptions->tGameInfo.bServerSideHealth) {
363 // Cheat prevention check (God Mode etc), make sure killer is the host or the packet is sent by the client owning the worm
364 if (!cl->isLocalClient()) {
365 if (cl->OwnsWorm(victim)) { // He wants to die, let's fulfill his dream ;)
366 CWorm *w = cClient->getRemoteWorms() + victim;
367 if (!w->getAlreadyKilled()) // Prevents killing the worm twice (once by server and once by the client itself)
368 cClient->getNetEngine()->SendDeath(victim, killer);
369 } else {
370 warnings << "GameServer::ParseDeathPacket: victim " << victim << " is not one of the clients (" << cl->debugName(true) << ") worms, killer was " << killer << endl;
371 server->netError = "ParseDeathPacket with invalid victim + SSH";
372 }
373
374 // The client on this machine will send the death again, then we'll parse it
375 return;
376 }
377 } else {
378 // Cheat prevention check: make sure the victim is one of the client's worms
379 if (!cl->OwnsWorm(victim)) {
380 warnings << "GameServer::ParseDeathPacket: victim " << victim << " is not one of the clients (" << cl->debugName(true) << "), killer was " << killer << endl;
381 server->netError = "ParseDeathPacket with invalid victim";
382 return;
383 }
384 }
385
386 // A small hack: multiple-suiciding can lag down the game, check if
387 // the packet contains another death (with same killer and victim), if so, just
388 // increase the suicide variable and proceed
389 if (killer == victim) {
390 server->iSuicidesInPacket++;
391 if (!bs->isPosAtEnd()) {
392 if (bs->peekByte() == C2S_DEATH) {
393 std::string s = bs->peekData(3);
394 if (s.size() == 3) {
395 s.erase(s.begin()); // The C2S_DEATH byte
396 if (((uchar)s[0] == (uchar)victim) && ((uchar)s[1] == (uchar)killer))
397 return;
398 }
399 }
400 }
401 }
402
403 server->killWorm(victim, killer, server->iSuicidesInPacket);
404
405 server->iSuicidesInPacket = 0; // Reset counter, so next /suicide command will work correctly
406 }
407
408
409 ///////////////////
410 // Parse a chat text packet
ParseChatText(CBytestream * bs)411 void CServerNetEngine::ParseChatText(CBytestream *bs) {
412 std::string buf = Utf8String(bs->readString());
413
414 if(cl->getNumWorms() == 0 && !cl->isLocalClient()) {
415 warnings << cl->debugName() << " with no worms sends message '" << buf << "'" << endl;
416 return;
417 }
418
419 if (buf.empty()) { // Ignore empty messages
420 warnings << cl->debugName() << " sends empty message" << endl;
421 return;
422 }
423
424 //Check message length.
425 //OLX seems to allow sending arbitrary long chat messages.
426 //These messages can be a problem as they can cause excessive lag.
427 //TODO: Limit message length client side too?
428 if ((int)buf.size() > tLXOptions->iMaxChatMessageLength) {
429 buf.resize(tLXOptions->iMaxChatMessageLength);
430 }
431
432
433 // TODO: is it correct that we check here only for worm 0 ?
434 // TODO: should we perhaps also check, if the beginning of buf is really the correct name?
435
436 std::string command_buf = buf;
437 {
438 std::string startStr = (cl->getWorm(0) ? cl->getWorm(0)->getName() : "") + ": ";
439 if( strStartsWith(buf, startStr) )
440 command_buf = buf.substr(startStr.size()); // Special buffer used for parsing special commands (begin with /)
441 }
442
443 // Check for special commands
444 if (command_buf.size() > 2)
445 if (command_buf[0] == '/' && command_buf[1] != '/') { // When two slashes at the beginning, parse as a normal message
446 ParseChatCommand(command_buf);
447 return;
448 }
449
450 //Previously, OLX logged all chat to the main log when hosting a server.
451 //It clutters the log so now log only if configured to do so.
452 //NOTE: This has nothing to do with the "Log conversations" option - it goes to different log!
453 if (tLXOptions->bLogServerChatToMainlog) {
454 if(!strStartsWith(command_buf, "!login") && !strStartsWith(command_buf, "//login")) // people could try wrong chat command
455 notes << "CHAT: " << buf << endl;
456 }
457
458 // Check for Clx (a cheating version of lx)
459 if(buf[0] == 0x04) {
460 server->SendGlobalText(cl->debugName() + " seems to have CLX or some other hack", TXT_NORMAL);
461 }
462
463 // Don't send text from muted players
464 if (cl->getMuted()) {
465 notes << "ignored message from muted " << cl->debugName() << endl;
466 return;
467 }
468
469 if( !cl->isLocalClient() ) {
470 // Check if player tries to fake other player
471 bool nameMatch = false;
472 for( int i=0; i<cl->getNumWorms(); i++ )
473 if( buf.find(cl->getWorm(i)->getName() + ": ") == 0 )
474 nameMatch = true;
475 if( !nameMatch )
476 {
477 notes << "Client " << cl->debugName() << " probably tries to fake other player, or an old client uses /me cmd" << endl;
478 return;
479 }
480 }
481
482 server->SendGlobalText(buf, TXT_CHAT);
483
484 if( DedicatedControl::Get() && buf.size() > cl->getWorm(0)->getName().size() + 2 )
485 DedicatedControl::Get()->ChatMessage_Signal(cl->getWorm(0),buf.substr(cl->getWorm(0)->getName().size() + 2));
486 }
487
ParseChatCommandCompletionRequest(CBytestream * bs)488 void CServerNetEngineBeta7::ParseChatCommandCompletionRequest(CBytestream *bs) {
489 std::list<std::string> possibilities;
490
491 std::string startStr = bs->readString();
492 TrimSpaces(startStr);
493 std::vector<std::string> cmdStart = ParseCommandMessage("/" + startStr, false);
494
495 if(cmdStart.size() > 1) {
496 ChatCommand* cmd = GetCommand(cmdStart[0]);
497 if(!cmd) {
498 SendText("Chat auto completion: unknown command", TXT_NETWORK);
499 return;
500 }
501
502 // TODO: Move it out here and make it more general (add it to ChatCommand structure).
503 if(cmd->tProcFunc == &ProcessSetVar && cmdStart.size() == 2) {
504 for(CScriptableVars::const_iterator it = CScriptableVars::lower_bound(cmdStart[1]); it != CScriptableVars::Vars().end(); ++it) {
505 // ignore callbacks
506 if(it->second.var.type == SVT_CALLBACK) continue;
507
508 if( subStrCaseEqual(cmdStart[1], it->first, cmdStart[1].size()) ) {
509 std::string nextComplete = it->first.substr(0, cmdStart[1].size());
510 for(size_t f = cmdStart[1].size();; ++f) {
511 if(f >= it->first.size()) { nextComplete += ' '; break; }
512 nextComplete += it->first[f];
513 if(it->first[f] == '.') break;
514 }
515
516 if(possibilities.size() == 0 || *possibilities.rbegin() != nextComplete) {
517 possibilities.push_back(nextComplete);
518 }
519 }
520 else
521 break;
522 }
523
524 if(possibilities.size() == 0) {
525 SendText("Chat auto completion: unknown variable", TXT_NETWORK);
526 return;
527 }
528
529 if(possibilities.size() == 1) {
530 cl->getNetEngine()->SendChatCommandCompletionSolution(startStr, cmd->sName + " " + possibilities.front());
531 return;
532 }
533
534 size_t l = maxStartingEqualStr(possibilities);
535 if(l > cmdStart[1].size()) {
536 // we can complete to some longer sequence
537 cl->getNetEngine()->SendChatCommandCompletionSolution(startStr, cmd->sName + " " + possibilities.front().substr(0, l));
538 return;
539 }
540
541 // send list of all possibilities
542 size_t startSugPos = 0;
543 for(size_t p = 0; p < cmdStart[1].size(); ++p)
544 if(cmdStart[1][p] == '.') {
545 startSugPos = p + 1;
546 }
547 std::list<std::string> possNew;
548 while(possibilities.size() > 0) {
549 possNew.push_back(possibilities.front().substr(startSugPos));
550 possibilities.pop_front();
551 }
552 cl->getNetEngine()->SendChatCommandCompletionList(startStr, possNew);
553 return;
554 } // end autocomplete for setvar
555
556 // some hacks to autocomplete also some more commands
557 if(cmd->tProcFunc == &ProcessLevel) {
558 if(!cl->getRights()->ChooseLevel) {
559 SendText("You don't have the right to change the map", TXT_NETWORK);
560 return;
561 }
562 cmd = GetCommand("ded");
563 cmdStart.insert(cmdStart.begin(), "ded");
564 cmdStart[1] = "map";
565 }
566
567 if(cmd->tProcFunc == &ProcessMod) {
568 if(!cl->getRights()->ChooseMod) {
569 SendText("You don't have the right to change the mod", TXT_NETWORK);
570 return;
571 }
572 cmd = GetCommand("ded");
573 cmdStart.insert(cmdStart.begin(), "ded");
574 cmdStart[1] = "mod";
575 }
576
577 // autocomplete for dedicated
578 if(cmd->tProcFunc == &ProcessDedicated) {
579 std::string cmdToBeCompleted;
580 if(cmdStart.size() >= 2) cmdToBeCompleted = cmdStart[1]; // first is 'ded', second is ded-cmd
581 for(size_t i = 2; i < cmdStart.size(); ++i) {
582 cmdToBeCompleted += " \"" + cmdStart[i];
583 if(i != cmdStart.size() - 1) cmdToBeCompleted += "\"";
584 }
585
586 struct AutoCompleter : Task {
587 AutocompletionInfo info;
588 std::string oldChatCmd;
589 std::string cmdToBeCompleted;
590 struct Conn {
591 int connectionIndex; CServerNetEngine* cl;
592 Conn(int i, CServerNetEngine* c) : connectionIndex(i), cl(c) {}
593 } conn;
594
595 struct CLI : CmdLineIntf {
596 Conn conn;
597 CLI(const Conn& c) : conn(c) {}
598
599 virtual void pushReturnArg(const std::string& str) {}
600 virtual void finalizeReturn() {}
601 virtual void writeMsg(const std::string& msg, CmdLineMsgType type) {
602 // we need to do that from the gameloopthread
603 mainQueue->push(new MsgSender(conn.connectionIndex, conn.cl, msg));
604 }
605
606 } cli;
607
608 AutoCompleter(const std::string& old, const std::string& cmd, int i, CServerNetEngine* c)
609 : oldChatCmd(old), cmdToBeCompleted(cmd), conn(Conn(i,c)), cli(Conn(i,c)) {
610 name = "Chat command autocompleter";
611 }
612
613 struct AutocompleteSender : Action {
614 int connectionIndex;
615 CServerNetEngine* cl;
616 AutocompleteSender(int i, CServerNetEngine* c) : connectionIndex(i), cl(c) {}
617 bool checkValid() {
618 if(!cServer || !cServer->isServerRunning() || !cServer->getClients()) return false;
619 if(!cServer->getClients()[connectionIndex].getNetEngine()) return false;
620 if(cl != cServer->getClients()[connectionIndex].getNetEngine()) return false;
621 if(cServer->getClients()[connectionIndex].getStatus() == NET_DISCONNECTED) return false;
622 if(cServer->getClients()[connectionIndex].getStatus() == NET_ZOMBIE) return false;
623 return true;
624 }
625 };
626
627 struct SuggestionSender : AutocompleteSender {
628 std::string request;
629 std::string solution;
630 SuggestionSender(int i, CServerNetEngine* c, const std::string& req, const std::string& sol)
631 : AutocompleteSender(i,c), request(req), solution(sol) {}
632 int handle() {
633 if(!checkValid()) return -1;
634 cl->SendChatCommandCompletionSolution(request, solution);
635 return 0;
636 }
637 };
638
639 struct MsgSender : AutocompleteSender {
640 std::string msg;
641 MsgSender(int i, CServerNetEngine* c, const std::string& m) : AutocompleteSender(i,c), msg(m) {}
642 int handle() {
643 if(!checkValid()) return -1;
644 cl->SendText(msg, TXT_NOTICE);
645 return 0;
646 }
647 };
648
649 int handle() {
650 AutoComplete(cmdToBeCompleted, cmdToBeCompleted.size(), cli, info);
651 bool fail = false;
652 AutocompletionInfo::InputState replace;
653 info.popWait( AutocompletionInfo::InputState(cmdToBeCompleted), replace, fail );
654 std::string replaceStr = replace.text;
655 // another hack to have shorter completion
656 if(!strStartsWith(replaceStr, "map ") && !strStartsWith(replaceStr, "mod "))
657 replaceStr = "ded " + replaceStr;
658 if(replaceStr != "" && replaceStr[replaceStr.size()-1] == '\"')
659 replaceStr.erase(replaceStr.size()-1);
660 if(!fail)
661 // we need to do that from the gameloopthread
662 mainQueue->push(new SuggestionSender(conn.connectionIndex, conn.cl, oldChatCmd, replaceStr));
663 return 0;
664 }
665 };
666
667 int connectionIndex = getConnectionArrayIndex();
668 if(connectionIndex < 0) return;
669
670 taskManager->start(new AutoCompleter(startStr, cmdToBeCompleted, connectionIndex, this), true);
671 return;
672 } // end autocomplete for ded
673
674 return;
675 }
676 std::string match;
677 if(cmdStart.size() >= 1)
678 match = cmdStart[0];
679 stringlwr(match);
680
681 for (uint i=0; tKnownCommands[i].tProcFunc != NULL; ++i) {
682 if(subStrEqual(match, tKnownCommands[i].sName, match.size()))
683 possibilities.push_back(tKnownCommands[i].sName);
684 else if(subStrEqual(match, tKnownCommands[i].sAlias, match.size()))
685 possibilities.push_back(tKnownCommands[i].sAlias);
686 }
687
688 if(possibilities.size() == 0) {
689 SendText("Chat auto completion: unknown command", TXT_NETWORK);
690 return;
691 }
692
693 if(possibilities.size() == 1) {
694 // we have exactly one solution
695 ChatCommand* cmd = GetCommand(startStr);
696 if(cmd && startStr.size() <= possibilities.front().size()) {
697 SendText("Chat auto completion: " + cmd->sName + ": " +
698 itoa(cmd->iMinParamCount) + "-" + itoa(cmd->iMaxParamCount) + " params",
699 TXT_NETWORK);
700 }
701 else
702 cl->getNetEngine()->SendChatCommandCompletionSolution(startStr, possibilities.front() + " ");
703 return;
704 }
705
706 size_t l = maxStartingEqualStr(possibilities);
707 if(l > startStr.size()) {
708 // we can complete to some longer sequence
709 cl->getNetEngine()->SendChatCommandCompletionSolution(startStr, possibilities.front().substr(0, l));
710 return;
711 }
712
713 // send list of all possibilities
714 cl->getNetEngine()->SendChatCommandCompletionList(startStr, possibilities);
715 }
716
ParseAFK(CBytestream * bs)717 void CServerNetEngineBeta7::ParseAFK(CBytestream *bs) {
718
719 int wormid = bs->readByte();
720 int afkType = bs->readByte();
721 std::string message = bs->readString(128);
722 if (wormid < 0 || wormid >= MAX_WORMS)
723 return;
724
725 CWorm *w = &server->cWorms[wormid];
726 if( ! w->isUsed() || w->getClient() != cl )
727 return;
728
729 w->setAFK( (AFK_TYPE)afkType, message );
730
731 CBytestream bs1;
732 bs1.writeByte( S2C_AFK );
733 bs1.writeByte( wormid );
734 bs1.writeByte( afkType );
735 bs1.writeString( message );
736
737 CServerConnection *cl1;
738 int i;
739 for( i=0, cl1=server->cClients; i < MAX_CLIENTS; i++, cl1++ )
740 if( cl1->getStatus() == NET_CONNECTED && cl1->getClientVersion() >= OLXBetaVersion(7) )
741 cl1->getNetEngine()->SendPacket( &bs1 );
742 }
743
744
745 ///////////////////
746 // Parse a 'update lobby' packet
ParseUpdateLobby(CBytestream * bs)747 void CServerNetEngine::ParseUpdateLobby(CBytestream *bs) {
748 // Must be in lobby
749 if ( server->iState != SVS_LOBBY ) {
750 notes << "GameServer::ParseUpdateLobby: Not in lobby." << endl;
751
752 // Skip to get the right position
753 bs->Skip(1);
754
755 return;
756 }
757
758 bool ready = bs->readBool();
759 int i;
760
761 // Set the client worms lobby ready state
762 for (i = 0; i < cl->getNumWorms(); i++) {
763 cl->getWorm(i)->setLobbyReady( ready );
764 }
765
766 // Let all the worms know about the new lobby state
767 for( int i=0; i<MAX_CLIENTS; i++ )
768 server->cClients[i].getNetEngine()->SendUpdateLobby();
769 }
770
771
772 ///////////////////
773 // Parse a disconnect packet
ParseDisconnect()774 void CServerNetEngine::ParseDisconnect() {
775 // Check if the client hasn't already left
776 if (cl->getStatus() == NET_DISCONNECTED) {
777 notes << "GameServer::ParseDisconnect: Client has already disconnected." << endl;
778 return;
779 }
780
781 // Host cannot leave...
782 if (cl->isLocalClient()) {
783 if(cClient->getStatus() != NET_DISCONNECTED) {
784 warnings << "we got disconnect-package from local client but local client is not disconnected" << endl;
785 return; // ignore
786 }
787
788 /* There are several cases (e.g. in ParsePrepareGame) where we would
789 * just disconnect, even as the local client. It's hard to catch all
790 * these possible cases and in most cases, we have a screwed up
791 * network stream with the client.
792 * For that reason, we just try to reconnect it now.
793 */
794 warnings << "host-client disconnected, reconnecting now ..." << endl;
795 cClient->Reconnect();
796
797 return;
798 }
799
800 server->DropClient(cl, CLL_QUIT, "client disconnected");
801 }
802
803
804
805 ///////////////////
806 // Parse a 'grab bonus' packet
ParseGrabBonus(CBytestream * bs)807 void CServerNetEngine::ParseGrabBonus(CBytestream *bs) {
808 int id = bs->readByte();
809 int wormid = bs->readByte();
810 int curwpn = bs->readByte();
811
812 // Check
813 if (server->iState != SVS_PLAYING) {
814 notes << "GameServer::ParseGrabBonus: Not playing." << endl;
815 return;
816 }
817
818
819 // Worm ID ok?
820 if (wormid >= 0 && wormid < MAX_WORMS) {
821 CWorm *w = &server->cWorms[wormid];
822
823 // Bonus id ok?
824 if (id >= 0 && id < MAX_BONUSES) {
825 CBonus *b = &server->cBonuses[id];
826
827 if (b->getUsed()) {
828
829 // If it's a weapon, change the worm's current weapon
830 if (b->getType() == BNS_WEAPON) {
831
832 if (curwpn >= 0 && curwpn < w->getNumWeaponSlots()) {
833 wpnslot_t *wpn = w->getWeapon(curwpn);
834 const weapon_t* oldWeapon = wpn->Weapon;
835 if(b->getWeapon() >= 0 && b->getWeapon() < server->cGameScript->GetNumWeapons()) {
836 wpn->Weapon = server->cGameScript.get()->GetWeapons() + b->getWeapon();
837 wpn->Enabled = true;
838 wpn->Charge = 1;
839 wpn->Reloading = false;
840 }
841 else {
842 notes << "worm " << w->getID() << " selected invalid bonus weapon" << endl;
843 wpn->Weapon = NULL;
844 wpn->Enabled = false;
845 }
846
847 // handle worm shoot end if needed
848 if(oldWeapon && wpn->Weapon != oldWeapon && w->getWormState()->bShoot)
849 server->WormShootEnd(w, oldWeapon);
850 if (wpn->Weapon != oldWeapon) {
851 for ( int i = 0; i < MAX_CLIENTS; i++ ) {
852 if ( !server->getClients()[i].isConnected() || w->getClient() == &(server->getClients()[i]) )
853 continue;
854 server->SendWeapons(&(server->getClients()[i])); // Tell other clients about weapons of this worm, otherwise laser will not be drawn
855 }
856 }
857 }
858 }
859
860 // Tell all the players that the bonus is now gone
861 CBytestream bs;
862 bs.writeByte(S2C_DESTROYBONUS);
863 bs.writeByte(id);
864 server->SendGlobalPacket(&bs);
865
866 if( b->getType() == BNS_HEALTH && server->getClient(w->getID())->getClientVersion() < OLXBetaVersion(0,58,1) )
867 for( int i=0; i < MAX_CLIENTS; i++ )
868 if( server->cClients[i].isConnected() )
869 server->cClients[i].getNetEngine()->QueueReportDamage( w->getID(), -30, w->getID() ); // It's random between 10-50 actually, we're doing approximation here
870 } else {
871 notes << "GameServer::ParseGrabBonus: Bonus already destroyed." << endl;
872 }
873 } else {
874 notes << "GameServer::ParseGrabBonus: Invalid bonus ID" << endl;
875 }
876 } else {
877 notes << "GameServer::ParseGrabBonus: invalid worm ID" << endl;
878 }
879 }
880
ParseSendFile(CBytestream * bs)881 void CServerNetEngine::ParseSendFile(CBytestream *bs)
882 {
883 cl->setLastFileRequestPacketReceived( AbsTime() ); // Set time in the past to force sending next packet
884 if( cl->getUdpFileDownloader()->receive(bs) )
885 {
886 if( cl->getUdpFileDownloader()->isFinished() &&
887 ( cl->getUdpFileDownloader()->getFilename() == "GET:" || cl->getUdpFileDownloader()->getFilename() == "STAT:" ) )
888 {
889 cl->getUdpFileDownloader()->abortDownload(); // We can't provide that file or statistics on it
890 }
891 }
892 }
893
894 /////////////////
895 // Parse a command from chat
ParseChatCommand(const std::string & message)896 bool CServerNetEngine::ParseChatCommand(const std::string& message)
897 {
898 // Parse the message
899 const std::vector<std::string>& parsed = ParseCommandMessage(message, false);
900
901 // Invalid
902 if (parsed.size() == 0)
903 return false;
904
905 // Get the command
906 ChatCommand *cmd = GetCommand(parsed[0]);
907 if (!cmd) {
908 SendText("The command is not supported.", TXT_NETWORK);
909 return false;
910 }
911
912 // Check the params
913 size_t num_params = parsed.size() - 1;
914 if (num_params < cmd->iMinParamCount || num_params > cmd->iMaxParamCount) {
915 SendText("Invalid parameter count.", TXT_NETWORK);
916 return false;
917 }
918
919
920 //Previously, OLX logged all private messages and team chat to the main log when hosting a server.
921 //It clutters the log and can be considered a privacy violation.
922 //So now log only if configured to do so.
923 //NOTE: This has nothing to do with the "Log conversations" option - it goes to a different log!
924 //Other commands except login are logged as previously.
925 if ((cmd->tProcFunc != &ProcessLogin) && ((cmd->tProcFunc != &ProcessPrivate) ||
926 (tLXOptions->bLogServerChatToMainlog)) && ((cmd->tProcFunc != &ProcessTeamChat) || (tLXOptions->bLogServerChatToMainlog)))
927 notes << "ChatCommand from " << cl->debugName(true) << ": " << message << endl;
928
929 // Get the parameters
930 std::vector<std::string> parameters = std::vector<std::string>(parsed.begin() + 1, parsed.end());
931
932 // Process the command
933 std::string error = cmd->tProcFunc(parameters, (cl->getNumWorms() > 0) ? cl->getWorm(0)->getID() : -1);
934 if (error.size() != 0) {
935 SendText(error, TXT_NETWORK);
936 notes << "ChatCommand " << cmd->sName << " returned error: " << error << endl;
937 return false;
938 }
939
940 return true;
941 }
942
ParseReportDamage(CBytestream * bs)943 void CServerNetEngineBeta9::ParseReportDamage(CBytestream *bs)
944 {
945 int id = bs->readByte();
946 float damage = bs->readFloat();
947 int offenderId = bs->readByte();
948
949 if( server->iState != SVS_PLAYING )
950 return;
951
952 if( id < 0 || id >= MAX_WORMS || offenderId < 0 || offenderId >= MAX_WORMS )
953 return;
954
955 CWorm *w = & server->getWorms()[id];
956 CWorm *offender = & server->getWorms()[offenderId];
957
958 if( ! w->isUsed() || ! offender->isUsed() )
959 return;
960
961 if( ! cl->OwnsWorm(id) && ! cl->isLocalClient() ) // Allow local client to send damage for pre-Beta9 clients
962 return;
963
964 offender->addDamage( damage, w, tLXOptions->tGameInfo );
965
966 //notes << "CServerNetEngineBeta9::ParseReportDamage() offender " << offender->getID() << " dmg " << damage << " victim " << id << endl;
967 // Re-send the packet to all clients, except the sender
968 for( int i=0; i < MAX_CLIENTS; i++ )
969 if( server->cClients[i].getStatus() == NET_CONNECTED && (&server->cClients[i]) != cl )
970 server->cClients[i].getNetEngine()->QueueReportDamage( w->getID(), damage, offender->getID() );
971 }
972
973 /*
974 ===========================
975
976 Connectionless packets
977
978 ===========================
979 */
980
981
982 ///////////////////
983 // Parses connectionless packets
ParseConnectionlessPacket(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bs,const std::string & ip)984 void GameServer::ParseConnectionlessPacket(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bs, const std::string& ip) {
985 std::string cmd = bs->readString(128);
986
987 if (cmd == "lx::getchallenge")
988 ParseGetChallenge(tSocket, bs);
989 else if (cmd == "lx::connect")
990 ParseConnect(tSocket, bs);
991 else if (cmd == "lx::ping")
992 ParsePing(tSocket);
993 else if (cmd == "lx::time") // request for cServer->fServertime
994 ParseTime(tSocket);
995 else if (cmd == "lx::query")
996 ParseQuery(tSocket, bs, ip);
997 else if (cmd == "lx::getinfo")
998 ParseGetInfo(tSocket);
999 else if (cmd == "lx::wantsjoin")
1000 ParseWantsJoin(tSocket, bs, ip);
1001 else if (cmd == "lx::traverse")
1002 ParseTraverse(tSocket, bs, ip);
1003 else if (cmd == "lx::registered")
1004 ParseServerRegistered(tSocket, bs);
1005 else {
1006 warnings << "GameServer::ParseConnectionlessPacket: unknown packet \"" << cmd << "\"" << endl;
1007 bs->SkipAll(); // Safety: ignore any data behind this unknown packet
1008 }
1009 }
1010
1011
1012 ///////////////////
1013 // Handle a "getchallenge" msg
ParseGetChallenge(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bs_in)1014 void GameServer::ParseGetChallenge(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bs_in) {
1015 int i;
1016 NetworkAddr adrFrom;
1017 AbsTime OldestTime = AbsTime::Max();
1018 int ChallengeToSet = -1;
1019 CBytestream bs;
1020
1021 //hints << "Got GetChallenge packet" << endl;
1022
1023 adrFrom = tSocket->remoteAddress();
1024
1025 std::string client_version;
1026 if( ! bs_in->isPosAtEnd() )
1027 client_version = bs_in->readString(128);
1028
1029 if( Version(client_version).isBanned() ) {
1030 // TODO: move this out here
1031 bs.writeInt(-1, 4);
1032 bs.writeString("lx::badconnect");
1033 bs.writeString("Your " + client_version + " support was dropped, please download a new version at http://openlierox.net/");
1034 bs.Send(tSocket);
1035 notes << "GameServer::ParseGetChallenge: client has version " + client_version + " which is not supported." << endl;
1036 return;
1037 }
1038
1039 if( tLXOptions->bForceCompatibleConnect ) {
1040 std::string incompReason;
1041 if(!isVersionCompatible(Version(client_version), &incompReason)) {
1042 // TODO: move this out here
1043 bs.writeInt(-1, 4);
1044 bs.writeString("lx::badconnect");
1045 bs.writeString("Your " + client_version + " is incompatible with the server, please download a new version at http://openlierox.net/.\n"
1046 "Incompatibility reason: " + incompReason);
1047 bs.Send(tSocket);
1048 notes << "GameServer::ParseGetChallenge: client has incompatible version " << client_version << ": " << incompReason << endl;
1049 return;
1050 }
1051 }
1052
1053 // If were in the game, deny challenges
1054 if ( iState != SVS_LOBBY && !serverAllowsConnectDuringGame() ) {
1055 // TODO: move this out here
1056 bs.writeInt(-1, 4);
1057 bs.writeString("lx::badconnect");
1058 bs.writeString(OldLxCompatibleString(networkTexts->sGameInProgress));
1059 bs.Send(tSocket.get());
1060 notes << "GameServer::ParseGetChallenge: Client from " << tSocket->debugString() << " cannot join, the game is in progress." << endl;
1061 return;
1062 }
1063
1064 // see if we already have a challenge for this ip
1065 for (i = 0;i < MAX_CHALLENGES;i++) {
1066
1067 if (IsNetAddrValid(tChallenges[i].Address)) {
1068 if (AreNetAddrEqual(adrFrom, tChallenges[i].Address))
1069 continue;
1070 if (ChallengeToSet < 0 || tChallenges[i].fTime < OldestTime) {
1071 OldestTime = tChallenges[i].fTime;
1072 ChallengeToSet = i;
1073 }
1074 } else {
1075 ChallengeToSet = i;
1076 break;
1077 }
1078 }
1079
1080 if (ChallengeToSet >= 0) {
1081
1082 // overwrite the oldest
1083 tChallenges[ChallengeToSet].iNum = (rand() << 16) ^ rand();
1084 tChallenges[ChallengeToSet].Address = adrFrom;
1085 tChallenges[ChallengeToSet].fTime = tLX->currentTime;
1086 tChallenges[ChallengeToSet].sClientVersion = client_version;
1087
1088 i = ChallengeToSet;
1089 }
1090
1091 // Send the challenge details back to the client
1092 tSocket->setRemoteAddress(adrFrom);
1093
1094
1095 // TODO: move this out here
1096 bs.writeInt(-1, 4);
1097 bs.writeString("lx::challenge");
1098 bs.writeInt(tChallenges[i].iNum, 4);
1099 if( client_version != "" )
1100 bs.writeString(GetFullGameName());
1101 bs.Send(tSocket.get());
1102 }
1103
1104
1105 ///////////////////
1106 // Handle a 'connect' message
ParseConnect(const SmartPointer<NetworkSocket> & net_socket,CBytestream * bs)1107 void GameServer::ParseConnect(const SmartPointer<NetworkSocket>& net_socket, CBytestream *bs) {
1108 NetworkAddr adrFrom;
1109 int p;
1110 int numplayers;
1111 CServerConnection *newcl = NULL;
1112
1113 //hints << "Got Connect packet" << endl;
1114
1115 // Connection details
1116 int ProtocolVersion;
1117 //int Port = LX_PORT;
1118 int ChallId;
1119 int iNetSpeed = 0;
1120
1121
1122 // Ignore if we are playing (the challenge should have denied the client with a msg)
1123 if ( !serverAllowsConnectDuringGame() && iState != SVS_LOBBY ) {
1124 // if (iState == SVS_PLAYING) {
1125 notes << "GameServer::ParseConnect: In game, ignoring." << endl;
1126 return;
1127 }
1128
1129 // User Info to get
1130 adrFrom = net_socket->remoteAddress();
1131 std::string addrFromStr;
1132 NetAddrToString(adrFrom, addrFromStr);
1133
1134 // Check if this ip isn't already connected
1135 // HINT: this works in every case, even if there are two people behind one router
1136 // because the address ports are checked as well (router assigns a different port for each person)
1137 CServerConnection* reconnectFrom = NULL;
1138 p = 0;
1139 for(CServerConnection* cl = cClients;p<MAX_CLIENTS;p++,cl++) {
1140
1141 if(cl->getStatus() == NET_DISCONNECTED)
1142 continue;
1143
1144 if(AreNetAddrEqual(adrFrom, cl->getChannel()->getAddress())) {
1145 reconnectFrom = cl;
1146 break;
1147 }
1148 }
1149
1150
1151 // Read packet
1152 ProtocolVersion = bs->readInt(1);
1153 if (ProtocolVersion != PROTOCOL_VERSION) {
1154 hints << "Wrong protocol version " << ProtocolVersion << ", server protocol version is " << int(PROTOCOL_VERSION) << endl;
1155
1156 // Get the string to send
1157 std::string buf;
1158 if (networkTexts->sWrongProtocol != "<none>") {
1159 replacemax(networkTexts->sWrongProtocol, "<version>", itoa(PROTOCOL_VERSION), buf, 1);
1160 } else
1161 buf = " ";
1162
1163 // Wrong protocol version, don't connect client
1164 CBytestream bytestr;
1165 bytestr.writeInt(-1, 4);
1166 bytestr.writeString("lx::badconnect");
1167 bytestr.writeString(OldLxCompatibleString(buf));
1168 bytestr.Send(net_socket.get());
1169 hints << "GameServer::ParseConnect: Wrong protocol version" << endl;
1170 return;
1171 }
1172
1173 std::string szAddress;
1174 NetAddrToString(adrFrom, szAddress);
1175
1176 // Is this IP banned?
1177 if (getBanList()->isBanned(szAddress)) {
1178 hints << "Banned client " << szAddress << " was trying to connect" << endl;
1179 CBytestream bytestr;
1180 bytestr.writeInt(-1, 4);
1181 bytestr.writeString("lx::badconnect");
1182 bytestr.writeString(OldLxCompatibleString(networkTexts->sYouAreBanned));
1183 bytestr.Send(net_socket.get());
1184 return;
1185 }
1186
1187 //Port = pack->ReadShort();
1188 ChallId = bs->readInt(4);
1189 iNetSpeed = bs->readInt(1);
1190
1191 // Make sure the net speed is within bounds, because it's used for indexing
1192 iNetSpeed = CLAMP(iNetSpeed, 0, 3);
1193
1194 // Get user info
1195 int numworms = bs->readInt(1);
1196
1197 //Block attempts to join without any "worms" - this fixes the "empty name join glitch"
1198 //NOTE: The local client should we allowed to connect without worms in dedicated mode??
1199 //NOTE: Is this the correct/best way to do it?
1200 if (numworms<=0 && !(addrFromStr.find("127.0.0.1")==0)) {
1201 CBytestream sKickmsg;
1202 sKickmsg.writeInt(-1, 4);
1203 sKickmsg.writeString("lx::badconnect");
1204 sKickmsg.writeString(OldLxCompatibleString("Connection failed - you must have a name"));
1205 sKickmsg.Send(net_socket.get());
1206 return;
1207 }
1208
1209
1210 numworms = CLAMP(numworms, 0, (int)MAX_PLAYERS);
1211
1212 Version clientVersion;
1213
1214 // If we ignored this challenge verification, there could be double connections
1215
1216 // See if the challenge is valid
1217 {
1218 bool valid_challenge = false;
1219 int i;
1220 for (i = 0; i < MAX_CHALLENGES; i++) {
1221 if (IsNetAddrValid(tChallenges[i].Address) && AreNetAddrEqual(adrFrom, tChallenges[i].Address)) {
1222
1223 if (ChallId == tChallenges[i].iNum) { // good
1224 SetNetAddrValid(tChallenges[i].Address, false); // Invalidate it here to avoid duplicate connections
1225 tChallenges[i].iNum = 0;
1226 valid_challenge = true;
1227 break;
1228 } else { // bad
1229 valid_challenge = false;
1230
1231 // HINT: we could receive another connect packet which will contain this challenge
1232 // and therefore get the worm connected twice. To avoid it, we clear the challenge here
1233 SetNetAddrValid(tChallenges[i].Address, false);
1234 tChallenges[i].iNum = 0;
1235 if(!reconnectFrom) {
1236 hints << "HINT: deleting a doubled challenge" << endl;
1237 }
1238
1239 // There can be more challanges from one client, if this one doesn't match,
1240 // perhaps some other does
1241 }
1242 }
1243 }
1244
1245 // Ran out of challenges
1246 if (!reconnectFrom && i == MAX_CHALLENGES ) {
1247 notes << "No connection verification for client found" << endl;
1248 CBytestream bytestr;
1249 bytestr.writeInt(-1, 4);
1250 bytestr.writeString("lx::badconnect");
1251 bytestr.writeString(OldLxCompatibleString(networkTexts->sNoIpVerification));
1252 bytestr.Send(net_socket.get());
1253 return;
1254 }
1255
1256 if (!reconnectFrom && !valid_challenge) {
1257 notes << "Bad connection verification of client" << endl;
1258 CBytestream bytestr;
1259 bytestr.writeInt(-1, 4);
1260 bytestr.writeString("lx::badconnect");
1261 bytestr.writeString(OldLxCompatibleString(networkTexts->sBadVerification));
1262 bytestr.Send(net_socket.get());
1263 return;
1264 }
1265
1266 if(reconnectFrom)
1267 clientVersion = reconnectFrom->getClientVersion();
1268 else
1269 clientVersion = tChallenges[i].sClientVersion;
1270 }
1271
1272
1273 newcl = NULL;
1274 if(reconnectFrom) {
1275 // HINT: just let the client connect again
1276 newcl = reconnectFrom;
1277 notes << "Reconnecting ";
1278 if(reconnectFrom->isLocalClient()) {
1279 bLocalClientConnected = false; // because we are reconnecting the client
1280 notes << "local ";
1281 }
1282 notes << "client " << reconnectFrom->debugName() << endl;
1283
1284 if(reconnectFrom->isLocalClient() && addrFromStr.find("127.0.0.1") != 0) {
1285 errors << "client cannot be the local client because it has address " << addrFromStr << endl;
1286 bLocalClientConnected = true; // we just assume that
1287 reconnectFrom->setLocalClient(false);
1288 }
1289
1290 /*
1291 // Must not have got the connection good packet
1292 if(cl->getStatus() == NET_CONNECTED) {
1293 hints << "Duplicate connection" << endl;
1294
1295 // Resend the 'good connection' packet
1296 bytestr.Clear();
1297 bytestr.writeInt(-1,4);
1298 bytestr.writeString("lx::goodconnection");
1299 // Send the worm ids
1300 for( int i=0; i<cl->getNumWorms(); i++ )
1301 bytestr.writeInt(cl->getWorm(i)->getID(), 1);
1302 bytestr.Send(net_socket);
1303 return;
1304 }
1305
1306 // TODO: does this make sense? the already connected client tries to connect again while playing?
1307 // Trying to connect while playing? Drop the client
1308 if(cl->getStatus() == NET_PLAYING) {
1309 //conprintf("Client tried to reconnect\n");
1310 //DropClient(&players[p]);
1311 return;
1312 }
1313 */
1314 }
1315
1316 // Find a spot for the client
1317 p=0;
1318 if(newcl == NULL)
1319 for (CServerConnection* cl = cClients; p < MAX_CLIENTS; p++, cl++) {
1320 if (cl->getStatus() == NET_DISCONNECTED) {
1321 newcl = cl;
1322 break;
1323 }
1324 }
1325
1326
1327 // Calculate number of current players on this server
1328 numplayers = 0;
1329 for (p = 0; p < MAX_WORMS; p++) {
1330 if (cWorms[p].isUsed())
1331 numplayers++;
1332 }
1333 if(numplayers != iNumPlayers)
1334 warnings << "WARNING: stored player count " << iNumPlayers << " is different from recalculated player count " << numplayers << endl;
1335
1336 // Ran out of slots
1337 if (!newcl) {
1338 warnings << "I have no more open slots for the new client" << endl;
1339 notes << GetDateTime() << " - Server Error report" << endl;
1340 notes << "currentTime is " << (tLX->currentTime - AbsTime()).seconds() << " Numplayers is " << numplayers << endl;
1341 std::string msg;
1342
1343 CBytestream bytestr;
1344 bytestr.writeInt(-1, 4);
1345 bytestr.writeString("lx::badconnect");
1346 bytestr.writeString(OldLxCompatibleString(networkTexts->sNoEmptySlots));
1347 bytestr.Send(net_socket.get());
1348 return;
1349 }
1350
1351 // Server full (maxed already, or the number of extra worms wanting to join will go over the max)
1352 int max_players = (tLX->iGameType == GME_HOST ? tLXOptions->tGameInfo.iMaxPlayers : MAX_WORMS); // No limits (almost) for local play
1353 if (!newcl->isLocalClient() && numplayers + numworms > max_players) {
1354 notes << "I am full, so the new client cannot join" << endl;
1355 CBytestream bytestr;
1356 bytestr.writeInt(-1, 4);
1357 bytestr.writeString("lx::badconnect");
1358 bytestr.writeString(OldLxCompatibleString(networkTexts->sServerFull));
1359 bytestr.Send(net_socket.get());
1360 return;
1361 }
1362
1363
1364 // Connect
1365 if (!newcl)
1366 return;
1367
1368 // TODO: this is a bad hack, fix it
1369 // If this is the first client connected, it is our local client
1370 if (!bLocalClientConnected) {
1371 if (addrFromStr.find("127.0.0.1") == 0) { // Safety: check the IP
1372 newcl->setLocalClient(true);
1373 bLocalClientConnected = true;
1374 if(!reconnectFrom) // don't spam too much
1375 notes << "GameServer: our local client has connected" << endl;
1376 }
1377 } else {
1378 newcl->setLocalClient(false);
1379 }
1380
1381 if( newcl->getChannel() ) {
1382 // Note: do it also for reconnecting clients as reconnecting detection could be wrong
1383 // TODO: It seems that this happens very often. Why?
1384 //warnings << "ParseConnect: new client has old channel set" << endl;
1385 newcl->resetChannel();
1386 }
1387
1388 if( newcl->getChannel() == NULL) {
1389 if(! newcl->createChannel( std::min(clientVersion, GetGameVersion() ) ) )
1390 { // This should not happen - just in case
1391 errors << "Cannot create CChannel for client - invalid client version " << clientVersion.asString() << endl;
1392 CBytestream bytestr;
1393 bytestr.writeInt(-1, 4);
1394 bytestr.writeString("lx::badconnect");
1395 bytestr.writeString(OldLxCompatibleString("Your client is incompatible to this server"));
1396 bytestr.Send(net_socket.get());
1397 return;
1398 }
1399
1400 newcl->getChannel()->Create(adrFrom, net_socket);
1401 }
1402
1403 newcl->setLastReceived(tLX->currentTime);
1404 newcl->setNetSpeed(iNetSpeed);
1405
1406 newcl->setClientVersion( clientVersion );
1407
1408 newcl->setStatus(NET_CONNECTED);
1409
1410 if(newcl->getNetEngine()) {
1411 // Note: do it also for reconnecting clients as reconnecting detection could be wrong
1412 // TODO: It seems that this happens very often. Why?
1413 //warnings << "ParseConnect: old net engine was still set" << endl;
1414 newcl->resetNetEngine();
1415 }
1416 if(!newcl->getNetEngine())
1417 newcl->setNetEngineFromClientVersion(); // Deletes CServerNetEngine instance
1418 // WARNING/HINT/TODO: If we'll ever move ParseConnect() to CServerNetEngine this func should be called last, 'cause *this is deleted
1419
1420 if(!reconnectFrom)
1421 newcl->getRights()->Nothing(); // Reset the rights here
1422
1423 // check version compatibility already earlier to not add/kick bots if not needed
1424 if( iState != SVS_LOBBY ) {
1425 std::string msg;
1426 if(!checkVersionCompatibility(newcl, false, false, &msg)) {
1427 notes << "ParseConnect: " << newcl->debugName(false) << " is too old: " << msg << endl;
1428 CBytestream bytestr;
1429 bytestr.writeInt(-1, 4);
1430 bytestr.writeString("lx::badconnect");
1431 bytestr.writeString(OldLxCompatibleString("Your OpenLieroX version is too old, please update.\n" + msg));
1432 bytestr.Send(net_socket.get());
1433 RemoveClient(newcl, "version too old (while connecting)");
1434 return;
1435 }
1436 }
1437
1438
1439
1440 // prepare Welcome message
1441 std::string strWelcomeMessage = tLXOptions->sWelcomeMessage;
1442 if(strWelcomeMessage == "<none>") strWelcomeMessage = "";
1443 if(strWelcomeMessage != "") {
1444
1445 // Server name3
1446 replacemax(strWelcomeMessage, "<server>", tLXOptions->sServerName, strWelcomeMessage, 1);
1447
1448 // Host name
1449 replacemax(strWelcomeMessage, "<me>", cWorms[0].getName(), strWelcomeMessage, 1);
1450
1451 // Version
1452 replacemax(strWelcomeMessage, "<version>", clientVersion.asHumanString(), strWelcomeMessage, 1);
1453
1454 // Country
1455 bool hasDist = strWelcomeMessage.find("<distance>") != std::string::npos;
1456 if (strWelcomeMessage.find("<country>") != std::string::npos ||
1457 strWelcomeMessage.find("<city>") != std::string::npos ||
1458 strWelcomeMessage.find("<continent>") != std::string::npos ||
1459 hasDist) {
1460
1461 IpInfo info, localInfo;
1462 float dist = 0;
1463 std::string str_addr;
1464 NetAddrToString(newcl->getChannel()->getAddress(), str_addr);
1465 if (str_addr != "") {
1466 info = tIpToCountryDB->GetInfoAboutIP(str_addr);
1467 if (hasDist && sExternalIP.size()) {
1468 localInfo = tIpToCountryDB->GetInfoAboutIP(sExternalIP);
1469 dist = tIpToCountryDB->GetDistance(localInfo, info);
1470 replace(strWelcomeMessage, "<distance>", ftoa(dist, 0), strWelcomeMessage);
1471 }
1472
1473 replace(strWelcomeMessage, "<country>", info.countryName, strWelcomeMessage);
1474 replace(strWelcomeMessage, "<continent>", info.continent, strWelcomeMessage);
1475 replace(strWelcomeMessage, "<city>", info.city, strWelcomeMessage);
1476 }
1477 }
1478
1479 // Address
1480 std::string str_addr;
1481 NetAddrToString(newcl->getChannel()->getAddress(), str_addr);
1482 // Remove port
1483 size_t pos = str_addr.rfind(':');
1484 if (pos != std::string::npos)
1485 str_addr.erase(pos);
1486 replacemax(strWelcomeMessage, "<ip>", str_addr, strWelcomeMessage, 1);
1487 }
1488
1489
1490
1491 // Find spots in our list for the worms
1492 int ids[MAX_PLAYERS];
1493 for(int i = 0; i < MAX_PLAYERS; ++i) ids[i] = -1;
1494
1495 std::vector<WormJoinInfo> newWorms;
1496 newWorms.resize(numworms);
1497 for (int i = 0; i < numworms; i++) {
1498 newWorms[i].readInfo(bs);
1499
1500 // If bots aren't allowed, disconnect the client
1501 if (newWorms[i].m_type == PRF_COMPUTER && !tLXOptions->bAllowRemoteBots && !strincludes(szAddress, "127.0.0.1")) {
1502 hints << "Bot was trying to connect from " << newcl->debugName() << endl;
1503 CBytestream bytestr;
1504 bytestr.writeInt(-1, 4);
1505 bytestr.writeString("lx::badconnect");
1506 bytestr.writeString(OldLxCompatibleString(networkTexts->sBotsNotAllowed));
1507 bytestr.Send(net_socket.get());
1508
1509 RemoveClient(newcl, "bot tried to connect");
1510 return;
1511 }
1512
1513 //Check name length - by using tricks it's possible (at least in 0.58) to create excessively long names
1514 //which cause lots of annoyance. This server-side check prevents that and truncates oversized nicks.
1515 //Because the player creation menu allows max 20 characters, we can check it very easily.
1516 //Player name does not allow Unicode characters for backward compatibility
1517 if (newWorms[i].sName.size() > MAX_WORM_NAME_LENGTH)
1518 newWorms[i].sName.resize(MAX_WORM_NAME_LENGTH);
1519 }
1520
1521 std::set<CWorm*> removeWormList;
1522
1523 if(reconnectFrom) {
1524 for(int i = 0; i < reconnectFrom->getNumWorms(); ++i) {
1525 CWorm* w = reconnectFrom->getWorm(i);
1526 if(!w) {
1527 warnings << "ParseConnect with reconnecting client: worm nr " << i << " from client is unset" << endl;
1528 continue;
1529 }
1530 if(!w->isUsed()) {
1531 warnings << "ParseConnect with reconnecting client: worm nr " << i << " from client is not used" << endl;
1532 continue;
1533 }
1534
1535 bool found = false;
1536 for(int j = 0; j < numworms; ++j) {
1537 // compare by name, we have no possibility to do it more exact but it's also not that important
1538 if(ids[j] < 0 && newWorms[j].sName == w->getName()) {
1539 // found one
1540 found = true;
1541 ids[j] = w->getID();
1542 // HINT: Don't apply the other information from WormJoinInfo,
1543 // the worm should be up-to-date and we could screw it up (e.g. skin or team).
1544 break;
1545 }
1546 }
1547
1548 if(!found) {
1549 notes << "reconnecting client " << newcl->debugName(false) << " doesn't have worm ";
1550 notes << w->getID() << ":" << w->getName() << " anymore, removing that worm globally" << endl;
1551 removeWormList.insert(w);
1552 }
1553 }
1554 }
1555
1556 if(removeWormList.size() > 0) {
1557 RemoveClientWorms(newcl, removeWormList, "reconnected " + newcl->debugName(false) + " and worm does not exist anymore");
1558 }
1559
1560 std::set<CWorm*> newJoinedWorms;
1561
1562 // search slots for new worms
1563 for (int i = 0; i < numworms; ++i) {
1564 if(ids[i] >= 0) continue; // this worm is already associated
1565
1566 CWorm* w = AddWorm(newWorms[i]);
1567 if(w == NULL) {
1568 warnings << "Server:Connect: cannot add " << i << "th worm for " << newcl->debugName(false) << endl;
1569 break;
1570 }
1571
1572 w->setClient(newcl);
1573 ids[i] = w->getID();
1574 newJoinedWorms.insert(w);
1575
1576 hints << "Worm joined: " << w->getName();
1577 hints << " (id " << w->getID() << ",";
1578 hints << " from " << newcl->debugName(false) << ")" << endl;
1579
1580 // "Has connected" message
1581 if (networkTexts->sHasConnected != "<none>" && networkTexts->sHasConnected != "") {
1582 SendGlobalText(Replace(networkTexts->sHasConnected, "<player>", w->getName()), TXT_NETWORK);
1583 }
1584
1585 // Send the welcome message
1586 if(strWelcomeMessage != "")
1587 SendGlobalText(Replace(strWelcomeMessage, "<player>", w->getName()), TXT_NETWORK);
1588 }
1589
1590
1591 // Set the worm info
1592 newcl->setNumWorms(numworms);
1593 for (int i = 0;i < numworms;i++) {
1594 if(ids[i] < 0) {
1595 // Very strange, we should have catched this case earlier. This means that there were not enough open slots.
1596 errors << "Server::ParseConnect: We didn't found a slot for " << newWorms[i].sName << " for " << newcl->debugName(false) << endl;
1597 newcl->setWorm(i, NULL);
1598 continue;
1599 }
1600 newcl->setWorm(i, &cWorms[ids[i]]);
1601 }
1602
1603 // remove bots if not wanted anymore
1604 CheckForFillWithBots();
1605 numworms = newcl->getNumWorms(); // it was earlier when also local client could reconnect - should have no effect anymore
1606 net_socket->setRemoteAddress(adrFrom); // it could have been changed
1607
1608 {
1609 // Let em know they connected good
1610 CBytestream bytestr;
1611 bytestr.writeInt(-1, 4);
1612 bytestr.writeString("lx::goodconnection");
1613
1614 // Tell the client the id's of the worms
1615 for (int i = 0;i < newcl->getNumWorms(); i++)
1616 bytestr.writeInt(newcl->getWorm(i)->getID(), 1);
1617
1618 bytestr.Send(net_socket.get());
1619 }
1620
1621 // If we now the client version already, we can avoid this for newer clients.
1622 if(newcl->getClientVersion() <= OLXBetaVersion(4)) {
1623 // Let them know our version
1624 CBytestream bytestr;
1625 bytestr.writeInt(-1, 4);
1626 // sadly we have to send this because it was not thought about any forward-compatibility when it was implemented in Beta3
1627 // Server version is also added to challenge packet so client will receive it for sure (or won't connect at all).
1628 bytestr.writeString("lx::openbeta3");
1629 // sadly we have to send this for Beta4
1630 // we are sending the version string already in the challenge
1631 bytestr.writeInt(-1, 4);
1632 bytestr.writeString("lx::version");
1633 bytestr.writeString(GetFullGameName());
1634 bytestr.Send(net_socket.get());
1635 }
1636
1637 // In older clients, it was possible to disallow that (which doesn't really make sense).
1638 // In >=Beta9, we just always can use it. So we have to tell old clients that it's OK.
1639 if(newcl->getClientVersion() <= OLXVersion(0,57)) {
1640 CBytestream bytestr;
1641 bytestr.writeInt(-1, 4);
1642 bytestr.writeString("lx:mouseAllowed");
1643 bytestr.Send(net_socket.get());
1644 }
1645
1646 // TODO: why is this still done here and not via feature array or similar?
1647 // we do this since rev1897, i.e. since 0.57beta5
1648 if (tLXOptions->tGameInfo.bAllowStrafing) {
1649 CBytestream bytestr;
1650 bytestr.writeInt(-1, 4);
1651 bytestr.writeString("lx:strafingAllowed");
1652 bytestr.Send(net_socket.get());
1653 }
1654
1655 NotifyUserOnEvent(); // new player connected; if user is away, notify him
1656
1657 {
1658 // Send info about new worms to other clients.
1659 for(std::set<CWorm*>::iterator w = newJoinedWorms.begin(); w != newJoinedWorms.end(); ++w) {
1660 for(int j = 0; j < MAX_CLIENTS; ++j) {
1661 CServerConnection* cl = &cClients[j];
1662 if(cl == newcl) continue;
1663 if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE) continue;
1664 if(cl->getNetEngine() == NULL) continue;
1665 cl->getNetEngine()->SendUpdateWorm( *w );
1666 }
1667 }
1668 }
1669
1670 // Just inform everybody in case the client is not compatible.
1671 // If ingame, we kicked the player already earlier if not compatible.
1672 checkVersionCompatibility(newcl, false);
1673
1674
1675 if( iState == SVS_LOBBY )
1676 // Tell new client about game lobby details like mod/map and all game settings.
1677 // In game, we would send all these info in SendPrepareGame().
1678 newcl->getNetEngine()->SendUpdateLobbyGame();
1679
1680 // Update players listbox
1681 DeprecatedGUI::bHost_Update = true;
1682
1683 // Make host authorised
1684 if(newcl->isLocalClient())
1685 newcl->getRights()->Everything();
1686
1687 newcl->setConnectTime(tLX->currentTime);
1688
1689
1690 // handling for connect during game
1691 if( iState != SVS_LOBBY ) {
1692 // we already check for compatibility earlier
1693
1694 if(!reconnectFrom) {
1695 newcl->getShootList()->Clear();
1696 newcl->getUdpFileDownloader()->allowFileRequest(false);
1697 }
1698 newcl->setGameReady(false);
1699
1700 for(std::set<CWorm*>::iterator w = newJoinedWorms.begin(); w != newJoinedWorms.end(); ++w) {
1701 (*w)->Prepare(true);
1702 }
1703
1704 if(newcl->getClientVersion() <= OLXVersion(0,57))
1705 // HINT: this is necessary because of beta8 which doesn't update all its state variables from preparegame
1706 newcl->getNetEngine()->SendUpdateLobbyGame();
1707 newcl->getNetEngine()->SendPrepareGame();
1708 }
1709
1710 if (tLX->iGameType != GME_LOCAL) {
1711 if( iState == SVS_LOBBY )
1712 SendWormLobbyUpdate(); // to everbody
1713 }
1714
1715 // Tell all the connected clients the info about these worm(s)
1716 // Send all information about all worms to new client.
1717 UpdateWorms();
1718
1719
1720 if( iState != SVS_LOBBY ) {
1721 newcl->getNetEngine()->SendTeamScoreUpdate();
1722
1723 // TODO: what is the information of this hint? and does it apply here anyway?
1724 // Cannot send anything after S2C_PREPAREGAME because of bug in old 0.56 clients - Beta5+ does not have this bug
1725
1726 // In ParseImReady, we will inform other clients that this new client got ready.
1727 // Here, we inform the new client about other ready clients.
1728 // This sends also the weapons.
1729 CServerConnection *cl = cClients;
1730 for(int c = 0; c < MAX_CLIENTS; c++, cl++) {
1731 if(cl->getStatus() != NET_CONNECTED) continue;
1732 if(cl->getNumWorms() == 0) continue;
1733 if(!cl->getGameReady()) continue;
1734 cl->getNetEngine()->SendClientReady(newcl);
1735 }
1736
1737 /*
1738 // TODO: This should only be needed if gamemode or other stuff changed it to some unexpected value.
1739 // Should we perhaps change it? Or let gamemode (or other code) just take care about this anyway?
1740 // Set some info about the new worms
1741 for(std::set<CWorm*>::iterator w = newJoinedWorms.begin(); w != newJoinedWorms.end(); ++w) {
1742 for(int ii = 0; ii < MAX_CLIENTS; ii++)
1743 cClients[ii].getNetEngine()->SendWormScore( (*w) );
1744 }*/
1745
1746 m_flagInfo->sendCurrentState(newcl);
1747
1748 for(std::set<CWorm*>::iterator w = newJoinedWorms.begin(); w != newJoinedWorms.end(); ++w) {
1749 PrepareWorm(*w);
1750
1751 // send other clients non-default worm properties of new worms
1752
1753 if(CServerNetEngine::isWormPropertyDefault(*w)) continue;
1754
1755 for( int i = 0; i < MAX_CLIENTS; i++ ) {
1756 if( newcl == &cClients[i] ) continue;
1757 if( cClients[i].getStatus() != NET_CONNECTED ) continue;
1758 cClients[i].getNetEngine()->SendWormProperties(*w); // if we have changed them in prepare or so
1759 }
1760 }
1761
1762 newcl->getNetEngine()->SendWormProperties(true); // send new client other non-default worm properties
1763 }
1764
1765
1766
1767 // Share reliable channel bandwidth equally between all clients -
1768 // the amount of reliable data that should be transmitted is more-less the same for each client
1769 // But we finetuning the delay between reliable packets
1770 int clientsAmount = 0;
1771 for(int c = 0; c < MAX_CLIENTS; c++)
1772 {
1773 if(cClients[c].getStatus() != NET_CONNECTED)
1774 continue;
1775 if( !cClients[c].isLocalClient() )
1776 clientsAmount ++;
1777 }
1778 for(int c = 0; c < MAX_CLIENTS; c++)
1779 {
1780 if(cClients[c].getStatus() != NET_CONNECTED)
1781 continue;
1782 // TODO: finetune this coefficients
1783 if( cClients[c].isLocalClient() )
1784 cClients[c].getChannel()->LimitReliableStreamBandwidth( -1.0f ); // No limit
1785 else
1786 cClients[c].getChannel()->LimitReliableStreamBandwidth( getMaxUploadBandwidth() / clientsAmount * 0.5f,
1787 4.0f + cClients[c].getNetSpeed() );
1788 }
1789 }
1790
1791
1792 ///////////////////
1793 // Parse a ping packet
ParsePing(const SmartPointer<NetworkSocket> & net_socket)1794 void GameServer::ParsePing(const SmartPointer<NetworkSocket>& net_socket)
1795 {
1796 // Ignore pings in local
1797 // HINT: this can happen when you quit your server and go play local immediatelly - some
1798 // people have not updated their serverlist yet and try to ping and query the server
1799 if (tLX->iGameType != GME_HOST)
1800 return;
1801
1802 NetworkAddr adrFrom = net_socket->remoteAddress();
1803 net_socket->reapplyRemoteAddress();
1804 // Send the challenge details back to the client
1805
1806 CBytestream bs;
1807
1808 bs.Clear();
1809 bs.writeInt(-1, 4);
1810 bs.writeString("lx::pong");
1811
1812 bs.Send(net_socket);
1813 }
1814
1815
1816 ///////////////////
1817 // Parse a fservertime request packet
ParseTime(const SmartPointer<NetworkSocket> & tSocket)1818 void GameServer::ParseTime(const SmartPointer<NetworkSocket>& tSocket)
1819 {
1820 // Ignore pings in local
1821 // HINT: this can happen when you quit your server and go play local immediatelly - some
1822 // people have not updated their serverlist yet and try to ping and query the server
1823 if (tLX->iGameType != GME_HOST)
1824 return;
1825
1826
1827 // Send the challenge details back to the client
1828 tSocket->reapplyRemoteAddress();
1829 CBytestream bs;
1830
1831 bs.Clear();
1832 bs.writeInt(-1, 4);
1833 bs.writeString("lx::timeis");
1834 bs.writeFloat( (float)fServertime.seconds() );
1835
1836 bs.Send(tSocket);
1837 }
1838
1839
1840 ///////////////////
1841 // Parse a "wants to join" packet
ParseWantsJoin(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bs,const std::string & ip)1842 void GameServer::ParseWantsJoin(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bs, const std::string& ip) {
1843
1844 std::string Nick = bs->readString();
1845 xmlEntityText(Nick);
1846
1847 // Ignore wants to join in local
1848 // HINT: this can happen when you quit your server and go play local immediatelly - some
1849 // people have not updated their serverlist yet and try to ping and query the server
1850 if (tLX->iGameType != GME_HOST)
1851 return;
1852
1853 // Allowed?
1854 if (!tLXOptions->bAllowWantsJoinMsg)
1855 return;
1856
1857 // Accept these messages from banned clients?
1858 if (!tLXOptions->bWantsJoinBanned && cBanList.isBanned(ip))
1859 return;
1860
1861 //Truncate nicks longer than "legitimately" possible
1862 if (Nick.size() > MAX_WORM_NAME_LENGTH)
1863 Nick.resize(MAX_WORM_NAME_LENGTH);
1864
1865 // Notify about the wants to join
1866 if (networkTexts->sWantsJoin != "<none>") {
1867 std::string buf;
1868 replacemax(networkTexts->sWantsJoin, "<player>", Nick, buf, 1);
1869 SendGlobalText(buf, TXT_NORMAL);
1870 }
1871 }
1872
1873
1874 ///////////////////
1875 // Parse a query packet
ParseQuery(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bs,const std::string & ip)1876 void GameServer::ParseQuery(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bs, const std::string& ip)
1877 {
1878 CBytestream bytestr;
1879
1880 int num = bs->readByte();
1881
1882 // Ignore queries in local
1883 // HINT: this can happen when you quit your server and go play local immediatelly - some
1884 // people have not updated their serverlist yet and try to ping and query the server
1885 if (tLX->iGameType != GME_HOST)
1886 return;
1887
1888 bytestr.writeInt(-1, 4);
1889 bytestr.writeString("lx::queryreturn");
1890
1891 // Get Port
1892 size_t pos = ip.rfind(':');
1893 if (pos != std::string::npos)
1894 ip.substr(pos);
1895
1896 //if(ip == "23401")
1897 // bytestr.writeString(OldLxCompatibleString(sName+" (private)")); // Not used anyway
1898 //else
1899 bytestr.writeString(OldLxCompatibleString(tLXOptions->sServerName));
1900 bytestr.writeByte(iNumPlayers);
1901 bytestr.writeByte(tLXOptions->tGameInfo.iMaxPlayers);
1902 bytestr.writeByte(iState);
1903 bytestr.writeByte(num);
1904 // Beta8+ info - old clients will just skip it
1905 bytestr.writeString( GetGameVersion().asString() );
1906 bytestr.writeByte( serverAllowsConnectDuringGame() );
1907
1908 bytestr.Send(tSocket);
1909 }
1910
1911
1912 ///////////////////
1913 // Parse a get_info packet
ParseGetInfo(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bsHeader)1914 void GameServer::ParseGetInfo(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bsHeader)
1915 {
1916 // Ignore queries in local
1917 // HINT: this can happen when you quit your server and go play local immediatelly - some
1918 // people have not updated their serverlist yet and try to ping and query the server
1919 if (tLX->iGameType != GME_HOST)
1920 return;
1921
1922 CBytestream bs;
1923
1924 if(bsHeader)
1925 bs.Append(bsHeader);
1926 else
1927 bs.writeInt(-1, 4);
1928 bs.writeString("lx::serverinfo");
1929
1930 bs.writeString(OldLxCompatibleString(tLXOptions->sServerName));
1931 bs.writeByte(tLXOptions->tGameInfo.iMaxPlayers);
1932 bs.writeByte(iState);
1933
1934 // TODO: check if we should append "levels/" string here, it was like this in old code
1935 bs.writeString( iState == SVS_PLAYING ? "levels/" + tLXOptions->tGameInfo.sMapFile : tLXOptions->tGameInfo.sMapFile );
1936 bs.writeString(tLXOptions->tGameInfo.sModName);
1937 bs.writeByte(getGameMode()->GeneralGameType());
1938 bs.writeInt16((tLXOptions->tGameInfo.iLives < 0) ? WRM_UNLIM : tLXOptions->tGameInfo.iLives);
1939 bs.writeInt16(tLXOptions->tGameInfo.iKillLimit);
1940 bs.writeInt16(tLXOptions->tGameInfo.iLoadingTime);
1941 bs.writeBool(tLXOptions->tGameInfo.bBonusesOn);
1942
1943
1944 // Players
1945 int numplayers = 0;
1946 int p;
1947 CWorm *w = cWorms;
1948 for (p = 0;p < MAX_WORMS;p++, w++) {
1949 if (w->isUsed())
1950 numplayers++;
1951 }
1952
1953 bs.writeByte(numplayers);
1954 w = cWorms;
1955 for (p = 0;p < MAX_WORMS;p++, w++) {
1956 if (w->isUsed()) {
1957 bs.writeString(RemoveSpecialChars(w->getName()));
1958 bs.writeInt(w->getScore(), 2);
1959 }
1960 }
1961
1962 w = cWorms;
1963 // Write out lives
1964 for (p = 0;p < MAX_WORMS;p++, w++) {
1965 if (w->isUsed())
1966 bs.writeInt(w->getLives(), 2);
1967 }
1968
1969 // Write out IPs
1970 w = cWorms;
1971 for (p = 0; p < MAX_WORMS; p++, w++) {
1972 if (w->isUsed()) {
1973 std::string addr = "0.0.0.0";
1974 // Disabled for privacy reasons
1975 /*
1976 if (NetAddrToString(w->getClient()->getChannel()->getAddress(), addr)) {
1977 size_t pos = addr.rfind(':');
1978 if (pos != std::string::npos)
1979 addr.erase(pos, std::string::npos);
1980 } else {
1981 errors << "Cannot convert address for worm " << w->getName() << endl;
1982 }
1983
1984 if (addr.size() == 0)
1985 addr = "0.0.0.0";
1986 */
1987 bs.writeString(addr);
1988 }
1989 }
1990
1991 // Write out my version (we do this since Beta5)
1992 bs.writeString(GetFullGameName());
1993
1994 // since Beta7
1995 bs.writeFloat(tLXOptions->tGameInfo.features[FT_GameSpeed]);
1996
1997 // since Beta9
1998 CServerNetEngineBeta9::WriteFeatureSettings(&bs);
1999
2000 // Game mode name
2001 bs.writeString(getGameMode()->Name());
2002
2003 bs.Send(tSocket);
2004 }
2005
2006
2007
2008 // Parse NAT traverse packet - can be received only with CServer::tSocket, send responce to one of tNatTraverseSockets[]
ParseTraverse(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bs,const std::string & ip)2009 void GameServer::ParseTraverse(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bs, const std::string& ip)
2010 {
2011 NetworkAddr adrFrom, adrClient;
2012
2013 // Get the server and the connecting client addresses
2014 adrFrom = tSocket->remoteAddress();
2015 std::string adrClientStr = bs->readString();
2016 if (!StringToNetAddr( adrClientStr, adrClient )) {
2017 errors << "GameServer: the address specified in ParseTraverse is invalid: " << adrClientStr << endl;
2018 return;
2019 }
2020
2021 // Send lx::traverse to udp server
2022 CBytestream bs1;
2023
2024 bs1.Clear();
2025 bs1.writeInt(-1, 4);
2026 bs1.writeString("lx::traverse");
2027 bs1.writeString(adrClientStr);
2028
2029 if( !bs->isPosAtEnd() )
2030 {
2031 // Some request sent over UDP masterserver
2032 std::string cmd = bs->readString();
2033 if (cmd == "lx::getinfo")
2034 ParseGetInfo(tSocket, &bs1); // TODO: it's pretty huge to be sent through our masterserver
2035 else if (cmd == "lx::wantsjoin")
2036 ParseWantsJoin(tSocket, bs, ip);
2037 return;
2038 }
2039 notes << "GameServer: Got a traverse from client " << adrClientStr << endl;
2040
2041 // Open a new connection for the client
2042 SmartPointer<NatConnection> newcl = new NatConnection();
2043 newcl->tAddress = adrClient;
2044 newcl->tTraverseSocket->OpenUnreliable(0);
2045 newcl->tConnectHereSocket->OpenUnreliable(0);
2046 if (!newcl->tTraverseSocket->isOpen()) {
2047 errors << "Could not open a new socket for a client connecting via NAT traversal: " << GetSocketErrorStr(GetSocketErrorNr()) << endl;
2048 return;
2049 }
2050
2051 if (!newcl->tTraverseSocket->Listen()) {
2052 errors << "Could not start listening on a NAT socket: " << GetSocketErrorStr(GetSocketErrorNr()) << endl;
2053 return;
2054 }
2055
2056
2057 /*bool conn_here =*/ newcl->tConnectHereSocket->Listen();
2058
2059 // Update the last used time
2060 newcl->fLastUsed = tLX->currentTime;
2061
2062 // Send traverse to server, to traverse socket
2063 newcl->tTraverseSocket->setRemoteAddress(adrFrom);
2064 bs1.Send(newcl->tTraverseSocket);
2065
2066 // Send ping to client to open NAT port
2067 bs1.Clear();
2068 bs1.writeInt(-1, 4);
2069 bs1.writeString("lx::pong");
2070
2071 //SetNetAddrPort(adrClient, (ushort)(port + i));
2072 newcl->tTraverseSocket->setRemoteAddress(adrClient);
2073 newcl->tConnectHereSocket->setRemoteAddress(adrClient);
2074
2075 // Send 3 times - first packet may be ignored by remote NAT
2076 bs1.Send(newcl->tTraverseSocket);
2077 bs1.Send(newcl->tTraverseSocket);
2078 bs1.Send(newcl->tTraverseSocket);
2079
2080 bs1.Clear();
2081 bs1.writeInt(-1, 4);
2082 bs1.writeString("lx::connect_here");
2083 bs1.Send(newcl->tConnectHereSocket);
2084
2085 tNatClients.push_back(newcl); // Add the client
2086 }
2087
2088 // Server sent us "lx::registered", that means it's alive - record that
ParseServerRegistered(const SmartPointer<NetworkSocket> & tSocket,CBytestream * bs)2089 void GameServer::ParseServerRegistered(const SmartPointer<NetworkSocket>& tSocket, CBytestream *bs)
2090 {
2091 if( tUdpMasterServers.size() == 0 )
2092 return;
2093 NetworkAddr addr, addr6;
2094 std::string domain = tUdpMasterServers[0].substr( 0, tUdpMasterServers[0].rfind(':') );
2095 int port = atoi(tUdpMasterServers[0].substr( tUdpMasterServers[0].rfind(':') + 1 ));
2096 if( !GetFromDnsCache(domain, addr, addr6) )
2097 return;
2098 SetNetAddrPort( addr, port );
2099 SetNetAddrPort( addr6, port );
2100
2101 if( tSocket->remoteAddress() == addr || tSocket->remoteAddress() == addr6 )
2102 iFirstUdpMasterServerNotRespondingCount = 0;
2103
2104 if( bs->isPosAtEnd() )
2105 return;
2106 std::string myAddr = bs->readString();
2107
2108 if( myAddr == "" )
2109 return;
2110 if (IsNetAddrV6(myAddr) == 0) {
2111 sServerAddressV6 = myAddr;
2112 } else {
2113 sServerAddressV4 = myAddr;
2114 }
2115 }
2116
2117