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