1 /////////////////////////////////////////
2 //
3 //             OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10 
11 
12 // Server class - Sending
13 // Created 1/7/02
14 // Jason Boettcher
15 
16 #include <vector>
17 
18 
19 #include "LieroX.h"
20 #include "CServer.h"
21 #include "Debug.h"
22 #include "StringUtils.h"
23 #include "CServerConnection.h"
24 #include "CServerNetEngine.h"
25 #include "Protocol.h"
26 #include "CWorm.h"
27 #include "Timer.h"
28 #include "Consts.h"
29 #include "CChannel.h"
30 #include "CMap.h"
31 #ifdef DEBUG
32 #include "MathLib.h"
33 #endif
34 #include "CGameMode.h"
35 
36 // declare them only locally here as nobody really should use them explicitly
37 std::string OldLxCompatibleString(const std::string &Utf8String);
38 
39 
40 ///////////////////
41 // Send a client a packet
SendPacket(CBytestream * bs)42 void CServerNetEngine::SendPacket(CBytestream *bs)
43 {
44 	if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE)
45 		return;
46 
47 	cl->getChannel()->AddReliablePacketToSend(*bs);
48 }
49 
50 ///////////////////
51 // Send all the clients a packet
SendGlobalPacket(CBytestream * bs)52 void GameServer::SendGlobalPacket(CBytestream *bs)
53 {
54 	// Assume reliable
55 	CServerConnection *cl = cClients;
56 	for(int c = 0; c < MAX_CLIENTS; c++, cl++) {
57 		if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE) continue;
58 		if(cl->getNetEngine() == NULL) continue;
59 		cl->getNetEngine()->SendPacket(bs);
60 	}
61 }
62 
SendGlobalPacket(CBytestream * bs,const Version & minVersion)63 void GameServer::SendGlobalPacket(CBytestream *bs, const Version& minVersion)
64 {
65 	// Assume reliable
66 	CServerConnection *cl = cClients;
67 	for(int c = 0; c < MAX_CLIENTS; c++, cl++) {
68 		if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE) continue;
69 		if(cl->getNetEngine() == NULL) continue;
70 		if(cl->getClientVersion() < minVersion) continue;
71 		cl->getNetEngine()->SendPacket(bs);
72 	}
73 }
74 
75 // TODO: This function is designed wrong. this->cl should be the receiver
76 // and the parameter should be the ready client.
SendClientReady(CServerConnection * receiver)77 void CServerNetEngine::SendClientReady(CServerConnection* receiver) {
78 	// Let everyone know this client is ready to play
79 
80 	//if(server->serverChoosesWeapons()) {
81 		// We don't necessarily have to send the weapons, we send them directly
82 		// (e.g. in cloneWeaponsToAllWorms() or in PrepareWorm()).
83 
84 		// But the client doesn't set CWorm::bGameReady=true, so we still
85 		// have to send this.
86 	//}
87 
88 	if ((receiver && receiver->getClientVersion() >= OLXBetaVersion(8)) || cl->getNumWorms() <= 2)  {
89 		CBytestream bytes;
90 		bytes.writeByte(S2C_CLREADY);
91 		bytes.writeByte(cl->getNumWorms());
92 		for (int i = 0;i < cl->getNumWorms();i++) {
93 			if(!cl->getWorm(i) || !cl->getWorm(i)->isUsed()) {
94 				errors << "SendClientReady: local worm nr " << i << " is wrong" << endl;
95 				goto SendReadySeperatly; // we cannot send them together
96 			}
97 
98 			// Send the weapon info here (also contains id)
99 			cl->getWorm(i)->writeWeapons(&bytes);
100 		}
101 
102 		if(receiver)
103 			receiver->getNetEngine()->SendPacket(&bytes);
104 		else
105 			server->SendGlobalPacket(&bytes);
106 
107 	} else { // old client && numworms > 2
108 
109 		// Note: LX56 assumes that a client can have only 2 worms.
110 
111 	SendReadySeperatly:
112 
113 		for (int i = 0; i < cl->getNumWorms(); i++) {
114 			if(!cl->getWorm(i) || !cl->getWorm(i)->isUsed()) {
115 				errors << "SendClientReady: local worm nr " << i << " is wrong" << endl;
116 				continue;
117 			}
118 
119 			CBytestream bytes;
120 			bytes.writeByte(S2C_CLREADY);
121 			bytes.writeByte(1);
122 			cl->getWorm(i)->writeWeapons(&bytes);
123 			if(receiver)
124 				receiver->getNetEngine()->SendPacket(&bytes);
125 			else
126 				server->SendGlobalPacket(&bytes);
127 		}
128 	}
129 }
130 
WritePrepareGame(CBytestream * bs)131 void CServerNetEngine::WritePrepareGame(CBytestream *bs)
132 {
133 	bs->writeByte(S2C_PREPAREGAME);
134 	// TODO: if that is always false, why do we have a variable for it?
135 	bs->writeBool(server->bRandomMap);	// Always false as of now
136 	if(!server->bRandomMap)
137 		bs->writeString("levels/" + tLXOptions->tGameInfo.sMapFile);
138 
139 	// Game info
140 	bs->writeInt(server->getGameMode()->GeneralGameType(),1);
141 	bs->writeInt16((tLXOptions->tGameInfo.iLives < 0) ? WRM_UNLIM : tLXOptions->tGameInfo.iLives);
142 	bs->writeInt16(tLXOptions->tGameInfo.iKillLimit);
143 	bs->writeInt16((int)(server->getGameMode()->TimeLimit() / 60.0f));
144 	bs->writeInt16(tLXOptions->tGameInfo.iLoadingTime);
145 	bs->writeBool(tLXOptions->tGameInfo.bBonusesOn);
146 	bs->writeBool(tLXOptions->tGameInfo.bShowBonusName);
147 	if(server->getGameMode()->GeneralGameType() == GMT_TIME)
148 		bs->writeInt16(tLXOptions->tGameInfo.iTagLimit);
149 	bs->writeString(tLXOptions->tGameInfo.sModDir);
150 
151 	server->cWeaponRestrictions.sendList(bs, server->cGameScript.get());
152 }
153 
SendPrepareGame()154 void CServerNetEngine::SendPrepareGame()
155 {
156 	CBytestream bs;
157 	WritePrepareGame(&bs);
158 	SendPacket( &bs );
159 }
160 
SendHideWorm(CWorm * worm,int forworm,bool show,bool immediate)161 void CServerNetEngine::SendHideWorm(CWorm *worm, int forworm,  bool show, bool immediate)
162 {
163 	// For old clients we move the worm out of the map and kill it
164 
165 	if(cl->getNumWorms() == 0 || cl->getWorm(0)->getID() != forworm)
166 		// ignore it
167 		return;
168 
169 	CBytestream bs;
170 
171 	// Hide the worm
172 	if (!show)  {
173 		//
174 		// Update the position
175 		//
176 
177 		if (cServer->getState() == SVS_PLAYING)  {
178 			bs.writeByte(S2C_UPDATEWORMS);
179 			bs.writeByte(1);  // Worm count
180 			bs.writeByte(worm->getID());
181 			bs.write2Int12(-20, -20);  // Position
182 			bs.writeInt(0, 1);  // Angle
183 			bs.writeByte(0);  // Flags
184 			bs.writeByte(0);  // Weapon
185 
186 			// Velocity
187 			if(cl->getClientVersion() >= OLXBetaVersion(5)) {
188 				bs.writeInt16(0);
189 				bs.writeInt16(0);
190 			}
191 
192 			// Send it reliably, this update is necessary
193 			SendPacket(&bs);
194 
195 			//
196 			// Kill
197 			//
198 			SendWormDied(worm);
199 		}
200 
201 	// Show the worm
202 	} else {
203 		SendSpawnWorm(worm, worm->getPos());
204 	}
205 }
206 
WritePrepareGame(CBytestream * bs)207 void CServerNetEngineBeta7::WritePrepareGame(CBytestream *bs)
208 {
209 	CServerNetEngine::WritePrepareGame(bs);
210 
211 	bs->writeFloat( tLXOptions->tGameInfo.features[FT_GameSpeed] );
212 
213 	// Set random weapons for spectating client, so it will skip weapon selection screen
214 	// Never do this for local client, local client must know correct state of serverChoosesWeapons!
215 	// TODO: it's hacky, don't have any ideas now how to make it nice
216 	bool spectate = cl->getNumWorms() > 0 && !cl->isLocalClient();
217 	if(spectate)
218 		for(int i = 0; i < cl->getNumWorms(); ++i)
219 			if(cl->getWorm(i) && !cl->getWorm(i)->isSpectating()) {
220 				spectate = false;
221 				break;
222 			}
223 
224 	bs->writeBool( server->serverChoosesWeapons() || spectate );
225 
226 	// We send random weapons from server in GameServer::StartGame()
227 	// TODO: Where are we doing that?
228 }
229 
WritePrepareGame(CBytestream * bs)230 void CServerNetEngineBeta9::WritePrepareGame(CBytestream *bs)
231 {
232 	CServerNetEngineBeta7::WritePrepareGame(bs);
233 
234 	bs->writeFloat(server->getGameMode()->TimeLimit() / 60.0f);
235 	WriteFeatureSettings(bs);
236 	bs->writeString(server->getGameMode()->Name());
237 
238 	// TODO: shouldn't this be somewhere in the clear function?
239 	cDamageReport.clear(); // In case something left from prev game
240 }
241 
242 
243 ///////////////////
244 // Send all the clients a string of text
SendGlobalText(const std::string & text,int type)245 void GameServer::SendGlobalText(const std::string& text, int type) {
246 	if(!cClients) {
247 		errors << "GS:SendGlobalText: clients not initialised" << endl;
248 		return;
249 	}
250 
251 	CServerConnection *cl = cClients;
252 	for(short c = 0; c < MAX_CLIENTS; c++, cl++) {
253 		if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE)
254 			continue;
255 
256 		cl->getNetEngine()->SendText(text, type);
257 	}
258 }
259 
260 
261 ///////////////////
262 // Send a client a string of text
SendText(const std::string & text,int type)263 void CServerNetEngine::SendText(const std::string& text, int type)
264 {
265 	CBytestream bs;
266 
267 	std::string nohtml_text = StripHtmlTags(text);
268 
269 	std::vector<std::string> split = splitstring(nohtml_text, 63, server->iState == SVS_LOBBY ? 600 : 300, tLX->cFont);
270 
271 	for (std::vector<std::string>::const_iterator it = split.begin(); it != split.end(); it++)  {
272 		// Send it
273 		bs.writeByte(S2C_TEXT);
274 		bs.writeInt(type, 1);
275 		bs.writeString(OldLxCompatibleString(*it));
276 	}
277 
278 	SendPacket(&bs);
279 }
280 
SendText(const std::string & text,int type)281 void CServerNetEngineBeta3::SendText(const std::string& text, int type)
282 {
283 	// For beta 3 - beta 7 (no HTML support but unlimited length support)
284 	CBytestream bs;
285 
286 	std::string nohtml_text = StripHtmlTags(text);
287 
288 	bs.writeByte(S2C_TEXT);
289 	bs.writeInt(type, 1);
290 	bs.writeString(OldLxCompatibleString(nohtml_text));
291 
292 	SendPacket(&bs);
293 }
294 
SendHideWorm(CWorm * worm,int forworm,bool show,bool immediate)295 void CServerNetEngineBeta3::SendHideWorm(CWorm *worm, int forworm, bool show, bool immediate)
296 {
297 	CServerNetEngine::SendHideWorm(worm, show, immediate);  // Just the same as for old LX
298 }
299 
SendText(const std::string & text,int type)300 void CServerNetEngineBeta8::SendText(const std::string& text, int type)
301 {
302 	// For beta 8+ (HTML support)
303 	CBytestream bs;
304 
305 	bs.writeByte(S2C_TEXT);
306 	bs.writeInt(type, 1);
307 	bs.writeString(OldLxCompatibleString(text));
308 
309 	SendPacket(&bs);
310 }
311 
SendChatCommandCompletionSolution(const std::string & startStr,const std::string & solution)312 void CServerNetEngineBeta7::SendChatCommandCompletionSolution(const std::string& startStr, const std::string& solution) {
313 	CBytestream bs;
314 
315 	bs.writeByte(S2C_CHATCMDCOMPLSOL);
316 	bs.writeString(startStr);
317 	bs.writeString(solution);
318 
319 	SendPacket(&bs);
320 }
321 
SendChatCommandCompletionList(const std::string & startStr,const std::list<std::string> & solutions)322 void CServerNetEngineBeta7::SendChatCommandCompletionList(const std::string& startStr, const std::list<std::string>& solutions) {
323 	// the solutions are for the last parameter of the command (or the command itself if no param is given)!
324 	CBytestream bs;
325 
326 	bs.writeByte(S2C_CHATCMDCOMPLLST);
327 	bs.writeString(startStr);
328 	bs.writeInt((uint)solutions.size(), 4);
329 	for(std::list<std::string>::const_iterator it = solutions.begin(); it != solutions.end(); ++it)
330 		bs.writeString(*it);
331 
332 	SendPacket(&bs);
333 }
334 
335 // send S2C_WORMSOUT
SendWormsOut(const std::list<byte> & ids)336 void CServerNetEngine::SendWormsOut(const std::list<byte>& ids) {
337 	if(ids.size() == 0) return; // ignore
338 
339 	CBytestream bs;
340 	bs.writeByte(S2C_WORMSOUT);
341 	bs.writeByte(ids.size());
342 
343 	for(std::list<byte>::const_iterator it = ids.begin(); it != ids.end(); ++it)
344 		bs.writeByte(*it);
345 
346 	SendPacket(&bs);
347 }
348 
349 // WARNING: When using this, be sure that we also drop the specific client. This is
350 // needed because the local worm amount of the client is different from ours in
351 // the meanwhile and it would screw up the network.
SendWormsOut(const std::list<byte> & ids)352 void GameServer::SendWormsOut(const std::list<byte>& ids)
353 {
354 	for(int c = 0; c < MAX_CLIENTS; c++) {
355 		CServerConnection* cl = &cClients[c];
356 		if (cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE)
357 			continue;
358 
359 		cl->getNetEngine()->SendWormsOut(ids);
360 	}
361 }
362 
363 ///////////////////
364 // Update all the client about the playing worms
365 // Returns true if we sent an update
SendUpdate()366 bool GameServer::SendUpdate()
367 {
368 	// Delays for different net speeds
369 	static const float	shootDelay[] = {0.010f, 0.005f, 0.0f, 0.0f};
370 
371 	//
372 	// Get the update packets for each worm that needs it and save them
373 	//
374 	std::list<CWorm *> worms_to_update;
375 	CWorm *w = cWorms;
376 	{
377 		int i, j;
378 		for (i = j = 0; j < iNumPlayers && i < MAX_WORMS; i++, w++)  {
379 			if (!w->isUsed())
380 				continue;
381 
382 			// HINT: this can happen when a new client joins during game and has not selected weapons yet
383 			if (w->getClient())
384 				if (!w->getClient()->getGameReady())
385 					continue;
386 
387 			++j;
388 
389 			// w is an own server-side copy of the worm-structure,
390 			// therefore we don't get problems by using the same checkPacketNeeded as client is also using
391 			if (w->checkPacketNeeded())  {
392 				worms_to_update.push_back(w);
393 			}
394 		}
395 	}
396 
397 	size_t uploadAmount = 0;
398 
399 	{
400 		int last = lastClientSendData;
401 		for (int i = 0; i < MAX_CLIENTS; i++)  {
402 			CServerConnection* cl = &cClients[ (i + lastClientSendData + 1) % MAX_CLIENTS ]; // fairly distribute the packets over the clients
403 
404 			if (cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE)
405 				continue;
406 
407 			// HINT: happens when clients join during game and haven't selected their weapons yet
408 			// HINT 2: this should be valid for beta 9+ though so the user can see others playing while selecting weapons
409 			if (cl->getClientVersion() < OLXBetaVersion(0,58,1))
410 				if (!cl->getGameReady())
411 					continue;
412 
413 			// Check if we have gone over the bandwidth rating for the client
414 			// If we have, just don't send a packet this frame
415 			if( !checkBandwidth(cl) ) {
416 				// We have gone over the bandwidth for the client, don't send a message this frame
417 				//hints << "Over bandwidth for client " << i << endl;
418 				continue;
419 			}
420 
421 			if(!cl->isLocalClient()) {
422 				// check our server bandwidth
423 				static Rate<100,5000> blockRate; // only for debug output
424 				static Rate<100,5000> blockRateAbs; // only for debug output
425 				blockRateAbs.addData(tLX->currentTime, 1);
426 				if(!checkUploadBandwidth(GetUpload() /* + uploadAmount */)) {
427 					// we have gone over our own bandwidth for non-local clients
428 					blockRate.addData(tLX->currentTime, 1);
429 					static AbsTime lastMessageTime = tLX->currentTime;
430 					if(tLX->currentTime - lastMessageTime > 30.0) {
431 						notes << "we got over the max upload bandwidth" << endl;
432 						notes << "   current upload is " << GetUpload() << " bytes/sec (last 2 secs)" << endl;
433 						notes << "   current short upload is " << GetUpload(0.1f) << " bytes/sec (last 0.1 sec)" << endl;
434 						notes << "   upload amount of this frame is " << uploadAmount << " bytes" << endl;
435 						if(blockRateAbs.getRate() > 0)
436 							notes << "   current block/update rate is " << float(100.0f * blockRate.getRate() / blockRateAbs.getRate()) << " % (last 5 secs)" << endl;
437 						lastMessageTime = tLX->currentTime;
438 					}
439 					continue;
440 				}
441 			}
442 
443 			CBytestream update_packets;  // Contains all the update packets except the one from this client
444 
445 			byte num_worms = 0;
446 
447 			// Send all the _other_ worms details
448 			{
449 				std::list<CWorm*>::const_iterator w_it = worms_to_update.begin();
450 				for(; w_it != worms_to_update.end(); w_it++) {
451 					CWorm* w = *w_it;
452 
453 					// Check if this client owns the worm
454 					if(cl->OwnsWorm(w->getID()))
455 						continue;
456 
457 					// Give the game mode a chance to override sending a packet (might reduce data sent)
458 					if(!getGameMode()->NeedUpdate(cl, w))
459 						continue;
460 
461 					++num_worms;
462 
463 					CBytestream bytes;
464 					bytes.writeByte(w->getID());
465 					w->writePacket(&bytes, true, cl);
466 
467 					// Send out the update
468 					update_packets.Append(&bytes);
469 				}
470 			}
471 
472 			CBytestream *bs = cl->getUnreliable();
473 			size_t oldBsPos = bs->GetPos();
474 
475 			// Write the packets to the unreliable bytestream
476 			bs->writeByte(S2C_UPDATEWORMS);
477 			bs->writeByte(num_worms);
478 			bs->Append(&update_packets);
479 
480 			// Write out a stat packet
481 			{
482 				bool need_send = false;
483 				{
484 					for (short j=0; j < cl->getNumWorms(); j++)
485 						if (cl->getWorm(j)->checkStatePacketNeeded())  {
486 							cl->getWorm(j)->updateStatCheckVariables();
487 							need_send = true;
488 							break;
489 						}
490 				}
491 
492 				// Only if necessary
493 				if (need_send)  {
494 					bs->writeByte( S2C_UPDATESTATS );
495 					bs->writeByte( cl->getNumWorms() );
496 					for(short j = 0; j < cl->getNumWorms(); j++)
497 						cl->getWorm(j)->writeStatUpdate(bs);
498 				}
499 			}
500 
501 			if(!cl->isLocalClient())
502 				uploadAmount += (bs->GetPos() - oldBsPos);
503 
504 			// Send the shootlist (reliable)
505 			CShootList *sh = cl->getShootList();
506 			float delay = shootDelay[cl->getNetSpeed()];
507 
508 			if(tLX->currentTime - sh->getStartTime() > delay && sh->getNumShots() > 0) {
509 				CBytestream shootBs;
510 
511 				// Send the shootlist
512 				if( sh->writePacket(&shootBs, cl->getClientVersion()) )
513 					sh->Clear();
514 
515 				if(!cl->isLocalClient())
516 					uploadAmount += shootBs.GetLength();
517 
518 				cl->getChannel()->AddReliablePacketToSend(shootBs);
519 			}
520 
521 			// TODO: that doesn't update uploadAmount
522 			cl->getNetEngine()->SendReportDamage();
523 
524 			if(!cl->isLocalClient())
525 				last = i;
526 		}
527 
528 		lastClientSendData = last;
529 	}
530 
531 	// All good
532 	return true;
533 }
534 
SendWeapons()535 void CServerNetEngine::SendWeapons()
536 {
537 	CBytestream bs;
538 
539 	CWorm* w = server->cWorms;
540 	for(int i = 0; i < MAX_WORMS; i++, w++) {
541 		if(!w->isUsed())
542 			continue;
543 		if(!w->getWeaponsReady())
544 			continue;
545 		bs.writeByte(S2C_WORMWEAPONINFO);
546 		w->writeWeapons(&bs);
547 	}
548 
549 	SendPacket(&bs);
550 }
551 
552 ///////////////////
553 // Send weapons to the client, or, if client is NULL, send to all clients
SendWeapons(CServerConnection * cl)554 void GameServer::SendWeapons(CServerConnection* cl)
555 {
556 	if(cl)
557 		cl->getNetEngine()->SendWeapons();
558 	else
559 		for(int c = 0; c < MAX_CLIENTS; c++)
560 			cClients[c].getNetEngine()->SendWeapons();
561 }
562 
563 // Send weapons of particular worm to everyone
SendWeapons(CWorm * w)564 void GameServer::SendWeapons(CWorm* w)
565 {
566 	if(!w->isUsed())
567 		return;
568 	if(!w->getWeaponsReady())
569 		return;
570 
571 	CBytestream bs;
572 	bs.writeByte(S2C_WORMWEAPONINFO);
573 	w->writeWeapons(&bs);
574 	SendGlobalPacket(&bs);
575 }
576 
577 ///////////////////
578 // Tells all clients that the worm is now tagged
SendWormTagged(CWorm * w)579 void GameServer::SendWormTagged(CWorm *w)
580 {
581 	// Check
582 	if (!w)  {
583 		errors << "A NULL worm passed to GameServer::SendWormTagged" << endl;
584 		return;
585 	}
586 
587 	// Build the packet
588 	CBytestream bs;
589 	bs.writeByte(S2C_TAGUPDATE);
590 	bs.writeInt(w->getID(), 1);
591 	bs.writeFloat((float)w->getTagTime().seconds());
592 
593 	// Send
594 	SendGlobalPacket(&bs);
595 }
596 
597 ///////////////////
598 // Check if we have gone over the clients bandwidth rating
599 // Returns true if we are under the bandwidth
checkBandwidth(CServerConnection * cl)600 bool GameServer::checkBandwidth(CServerConnection *cl)
601 {
602 	// Don't bother checking if the client is on the same comp as the server
603 	if( tLX->iGameType == GME_LOCAL )
604 		return true;
605 	if(cl->getNetSpeed() == 3) // local
606 		return true;
607 
608 
609 	// Modem, ISDN, LAN, local
610 	// (Bytes per second)
611 	const float	Rates[4] = {2500, 7500, 10000, 50000};
612 
613 	// Are we over the clients bandwidth rate?
614 	if(cl->getChannel()->getOutgoingRate() > Rates[cl->getNetSpeed()]) {
615 
616 		// Don't send the packet
617 		return false;
618 	}
619 
620 	// All ok
621 	return true;
622 }
623 
624 // true means we can send further data
checkUploadBandwidth(float fCurUploadRate)625 bool GameServer::checkUploadBandwidth(float fCurUploadRate) {
626 	if( tLX->iGameType == GME_LOCAL )
627 		return true;
628 
629 	float fMaxRate = getMaxUploadBandwidth();
630 
631 	{
632 		static bool didShowMessageAlready = false;
633 		if(!didShowMessageAlready)
634 			notes << "using max upload rate " << (fMaxRate / 1024.0f) << " kb/sec" << endl;
635 		didShowMessageAlready = true;
636 	}
637 
638 	return fCurUploadRate < fMaxRate;
639 }
640 
WriteFeatureSettings(CBytestream * bs)641 void CServerNetEngineBeta9::WriteFeatureSettings(CBytestream* bs) {
642 	int ftC = featureArrayLen();
643 	assert(ftC < 256*256);
644 	CBytestream bs1;
645 	int sendCount = 0;
646 	foreach( Feature*, f, Array(featureArray,ftC) )
647 	{
648 		if( f->get()->group < GIG_GameModeSpecific_Start ||
649 			f->get()->group == cServer->getGameMode()->getGameInfoGroupInOptions() )
650 		{
651 			if( tLXOptions->tGameInfo.features.hostGet(f->get()) == f->get()->unsetValue ) // Do not send a feature if it has default value = LX56 behavior
652 				continue;
653 			sendCount ++;
654 			bs1.writeString( f->get()->name );
655 			bs1.writeString( f->get()->humanReadableName );
656 			bs1.writeVar( tLXOptions->tGameInfo.features.hostGet(f->get()) );
657 			bs1.writeBool( tLXOptions->tGameInfo.features.olderClientsSupportSetting(f->get()) );
658 		}
659 	}
660 	bs->writeInt(sendCount, 2);
661 	bs->Append(&bs1);
662 }
663 
SendUpdateLobbyGame()664 void CServerNetEngine::SendUpdateLobbyGame()
665 {
666 	CBytestream bs;
667 	WriteUpdateLobbyGame(&bs);
668 	SendPacket(&bs);
669 }
670 
WriteUpdateLobbyGame(CBytestream * bs)671 void CServerNetEngine::WriteUpdateLobbyGame(CBytestream *bs)
672 {
673 	bs->writeByte(S2C_UPDATELOBBYGAME);
674 	bs->writeByte(MAX(tLXOptions->tGameInfo.iMaxPlayers,server->getNumPlayers()));  // This fixes the player disappearing in lobby
675 	bs->writeString(tLXOptions->tGameInfo.sMapFile);
676 	bs->writeString(tLXOptions->tGameInfo.sModName);
677 	bs->writeString(tLXOptions->tGameInfo.sModDir);
678 	bs->writeByte(server->getGameMode()->GeneralGameType());
679 	bs->writeInt16((tLXOptions->tGameInfo.iLives < 0) ? WRM_UNLIM : tLXOptions->tGameInfo.iLives);
680 	bs->writeInt16(tLXOptions->tGameInfo.iKillLimit);
681 	bs->writeInt16(tLXOptions->tGameInfo.iLoadingTime);
682 	bs->writeByte(tLXOptions->tGameInfo.bBonusesOn);
683 }
684 
WriteUpdateLobbyGame(CBytestream * bs)685 void CServerNetEngineBeta7::WriteUpdateLobbyGame(CBytestream *bs)
686 {
687 	CServerNetEngine::WriteUpdateLobbyGame(bs);
688 	bs->writeFloat(tLXOptions->tGameInfo.features[FT_GameSpeed]);
689 	bs->writeBool(tLXOptions->tGameInfo.bForceRandomWeapons);
690 	bs->writeBool(tLXOptions->tGameInfo.bSameWeaponsAsHostWorm);
691 }
692 
WriteUpdateLobbyGame(CBytestream * bs)693 void CServerNetEngineBeta9::WriteUpdateLobbyGame(CBytestream *bs)
694 {
695 	CServerNetEngineBeta7::WriteUpdateLobbyGame(bs);
696 	bs->writeFloat(server->getGameMode()->TimeLimit() / 60.0f);
697 	CServerNetEngineBeta9::WriteFeatureSettings(bs);
698 	bs->writeString(server->getGameMode()->Name());
699 }
700 
701 
702 ///////////////////
703 // Send an update of the game details in the lobby
UpdateGameLobby()704 void GameServer::UpdateGameLobby()
705 {
706 	if(getGameMode() == NULL) {
707 		errors << "Trying to play a non-existant gamemode" << endl;
708 		tLXOptions->tGameInfo.gameMode = GameMode(GM_DEATHMATCH);
709 	}
710 
711 	// Read map/mod name from map/mod file
712 	tLXOptions->tGameInfo.sMapName = CMap::GetLevelName(tLXOptions->tGameInfo.sMapFile);
713 	CGameScript::CheckFile(tLXOptions->tGameInfo.sModDir, tLXOptions->tGameInfo.sModName);
714 
715 	m_clientsNeedLobbyUpdate = true;
716 	m_clientsNeedLobbyUpdateTime = tLX->currentTime;
717 }
718 
SendUpdateLobby(CServerConnection * target)719 void CServerNetEngine::SendUpdateLobby(CServerConnection *target)
720 {
721     CBytestream bytestr;
722 
723     CServerConnection *cl = server->cClients;
724 	for(short c=0; c<MAX_CLIENTS; c++,cl++)
725 	{
726 	    if( target )
727 	    {
728     		cl = target;
729 			if( c != 0 )
730 				break;
731     	}
732 
733         if( cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE )
734             continue;
735 
736         // Set the client worms lobby ready state
737         bool ready = false;
738 	    for(short i=0; i < cl->getNumWorms(); i++) {
739 		    ready = cl->getWorm(i)->getLobbyReady();
740 	    }
741 
742 	    // Let all the worms know about the new lobby state
743 		if (cl->getNumWorms() <= 2)  { // Have to do this way because of bug in LX 0.56
744 			bytestr.writeByte(S2C_UPDATELOBBY);
745 			bytestr.writeByte(cl->getNumWorms());
746 			bytestr.writeByte(ready);
747 			for(short i=0; i<cl->getNumWorms(); i++) {
748 				bytestr.writeByte(cl->getWorm(i)->getID());
749 				bytestr.writeByte(cl->getWorm(i)->getTeam());
750 			}
751 		} else {
752 			int written = 0;
753 			while (written < cl->getNumWorms())  {
754 				bytestr.writeByte(S2C_UPDATELOBBY);
755 				bytestr.writeByte(1);
756 				bytestr.writeByte(ready);
757 				bytestr.writeByte(cl->getWorm(written)->getID());
758 				bytestr.writeByte(cl->getWorm(written)->getTeam());
759 				written++;
760 			}
761 		}
762     }
763 	SendPacket(&bytestr);
764 }
765 
766 ////////////////////////
767 // Hide a worm at receiver's screen
SendHideWorm(CWorm * worm,int forworm,bool show,bool immediate)768 void CServerNetEngineBeta9::SendHideWorm(CWorm *worm, int forworm, bool show, bool immediate)
769 {
770 	if (!worm)  {
771 		errors << "Invalid worm or receiver in SendHideWorm" << endl;
772 		return;
773 	}
774 
775 	if(forworm < 0 || forworm >= MAX_WORMS) {
776 		errors << "Invalid forworm in SendHideWorm" << endl;
777 		return;
778 	}
779 
780 	CBytestream bs;
781 	bs.writeByte(S2C_HIDEWORM);
782 	bs.writeByte(worm->getID());
783 	bs.writeByte(forworm);
784 	bs.writeBool(!show);  // True - hide, false - show
785 	bs.writeBool(immediate);  // True - immediate (no animation), false - animation
786 	SendPacket(&bs);
787 }
788 
789 ///////////////////
790 // Send updates for all the worm lobby states
SendWormLobbyUpdate(CServerConnection * receiver,CServerConnection * target)791 void GameServer::SendWormLobbyUpdate(CServerConnection* receiver, CServerConnection *target)
792 {
793 	if(receiver)
794 		receiver->getNetEngine()->SendUpdateLobby(target);
795 	else
796 		for( int i = 0; i < MAX_CLIENTS; i++ ) {
797 			if(!cClients[i].isUsed()) continue;
798 			cClients[i].getNetEngine()->SendUpdateLobby(target);
799 		}
800 }
801 
802 
803 ///////////////////
804 // Tell all the clients that we're disconnecting
SendDisconnect()805 void GameServer::SendDisconnect()
806 {
807 	CServerConnection *cl = cClients;
808 	if (!cl) // means we already shut down
809 		return;
810 
811 	CBytestream bs;
812 	bs.writeByte(S2C_LEAVING);
813 
814 	for(short c=0; c<MAX_CLIENTS; c++,cl++) {
815 		if(cl->getStatus() == NET_DISCONNECTED)
816 			continue;
817 
818 		// Send it out-of-bounds 3 times to make sure all the clients received it
819 		for(short i=0; i<3; i++)
820 			cl->getChannel()->Transmit(&bs);
821 	}
822 }
823 
824 
825 ///////////////////
826 // Update the worm name, skin, colour etc
SendUpdateWorm(CWorm * w)827 void CServerNetEngine::SendUpdateWorm( CWorm* w )
828 {
829 	CBytestream bytestr;
830 	bytestr.writeByte(S2C_WORMINFO);
831 	bytestr.writeInt(w->getID(), 1);
832 	w->writeInfo(&bytestr);
833 	SendPacket(&bytestr);
834 }
835 
836 // Version required to show question mark on damage popup number for older clients
SendUpdateWorm(CWorm * w)837 void CServerNetEngineBeta9::SendUpdateWorm( CWorm* w )
838 {
839 	CBytestream bytestr;
840 	bytestr.writeByte(S2C_WORMINFO);
841 	bytestr.writeInt(w->getID(), 1);
842 	w->writeInfo(&bytestr);
843 	bytestr.writeString(w->getClient()->getClientVersion().asString());
844 	SendPacket(&bytestr);
845 }
846 
UpdateWorm(CWorm * w)847 void GameServer::UpdateWorm(CWorm* w)
848 {
849 	for(int i = 0; i < MAX_CLIENTS; i++)
850 		cClients[i].getNetEngine()->SendUpdateWorm(w);
851 }
852 
853 ///////////////////
854 // Update the worm names, skins, colours etc
UpdateWorms()855 void GameServer::UpdateWorms()
856 {
857 	CWorm* w = cWorms;
858 	for(int i = 0; i < MAX_WORMS; i++, w++) {
859 		if(!w->isUsed())
860 			continue;
861 		UpdateWorm(w);
862 	}
863 }
864 
865 #ifdef FUZZY_ERROR_TESTING
866 ///////////////
867 // Used for testing network stability
SendRandomPacket()868 void GameServer::SendRandomPacket()
869 {
870 	CBytestream bs;
871 	int random_length = GetRandomInt(50);
872 	for (int i=0; i < random_length; i++)
873 		bs.writeByte((uchar)GetRandomInt(255));
874 
875 	CServerConnection* cl = cClients;
876 	for(short c=0; c<MAX_CLIENTS; c++,cl++) {
877 		if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE)
878 			continue;
879 
880 		// don't send these random clients to the local client
881 		if(cl->isLocalClient())
882 			continue;
883 
884 		cl->SendPacket(&bs);
885 	}
886 }
887 #endif
888 
889 static const float pingCoeff = 1.5f;	// Send another packet in minPing/pingCoeff milliseconds
890 static const int minPingDefault = 200;
891 
SendFiles()892 int CServerNetEngineBeta5::SendFiles()
893 {
894 	if(cl->getStatus() == NET_DISCONNECTED || cl->getStatus() == NET_ZOMBIE)
895 		return 0;
896 	int ping = 0;
897 	// That's a bit floody algorithm, it can be optimized I think
898 	if( cl->getUdpFileDownloader()->isSending() &&
899 		( cl->getChannel()->getBufferEmpty() ||
900 			( ! cl->getChannel()->getBufferFull() &&
901 			cl->getChannel()->getPing() != 0 &&
902 			tLX->currentTime - cl->getLastFileRequestPacketReceived() <= cl->getChannel()->getPing()/1000.0f / pingCoeff )) )
903 	{
904 		cl->setLastFileRequestPacketReceived( tLX->currentTime );
905 		CBytestream bs;
906 		bs.writeByte(S2C_SENDFILE);
907 		cl->getUdpFileDownloader()->send(&bs);
908 		cl->getNetEngine()->SendPacket( &bs );
909 		ping = minPingDefault; // Default assumed ping
910 		if( cl->getChannel()->getPing() != 0 )
911 			ping = cl->getChannel()->getPing();
912 	}
913 	return ping;
914 }
915 
SendFiles()916 void GameServer::SendFiles()
917 {
918 	// To keep sending packets if no acknowledge received from client -
919 	// process will pause for a long time otherwise, 'cause we're in GUI part
920 	bool startTimer = false;
921 	int minPing = minPingDefault;
922 
923 	for(int c = 0; c < MAX_CLIENTS; c++)
924 	{
925 		if(!cClients[c].getNetEngine()) continue;
926 		int ping = cClients[c].getNetEngine()->SendFiles();
927 		if( ping > 0 )
928 		{
929 			startTimer = true;
930 			if( minPing > ping )
931 				minPing = ping;
932 		}
933 	}
934 
935 	if( startTimer )
936 		Timer( "GS::SendFiles", null, NULL, (Uint32)(minPing / pingCoeff), true ).startHeadless();
937 }
938 
SendEmptyWeaponsOnRespawn(CWorm * Worm)939 void GameServer::SendEmptyWeaponsOnRespawn( CWorm * Worm )
940 {
941 	CBytestream bs;
942 	CServerConnection * cl = Worm->getClient();
943 	if(cl == NULL) {
944 		errors << "GS::SendEmptyWeaponsOnRespawn: client of worm " << Worm->getID() << ":" << Worm->getName() << " is unset" << endl;
945 		DumpConnections();
946 		return;
947 	}
948 	int i, j, curWeapon = Worm->getCurrentWeapon();
949 	for( i = 0; i < 5; i++ )
950 	{
951 		Worm->getWeapon(i)->Charge=0;
952 		Worm->getWeapon(i)->Reloading=1;
953 	}
954 	for( i=0; i<5; i++ )
955 	{
956 		if( i != curWeapon )
957 		{
958 			Worm->setCurrentWeapon(i);
959 			bs.writeByte( S2C_UPDATESTATS );
960 			bs.writeByte( cl->getNumWorms() );
961 			for( j = 0; j < cl->getNumWorms(); j++ )
962 				cl->getWorm(j)->writeStatUpdate(&bs);
963 		}
964 	}
965 	Worm->setCurrentWeapon(curWeapon);
966 	bs.writeByte( S2C_UPDATESTATS );
967 	bs.writeByte( cl->getNumWorms() );
968 	for( j = 0; j < cl->getNumWorms(); j++ )
969 		cl->getWorm(j)->writeStatUpdate(&bs);
970 	cl->getNetEngine()->SendPacket(&bs);
971 }
972 
SendSpawnWorm(CWorm * Worm,CVec pos)973 void CServerNetEngine::SendSpawnWorm(CWorm *Worm, CVec pos)
974 {
975 	server->getWorms()[Worm->getID()].setSpawnedOnce();
976 
977 	CBytestream bs;
978 	bs.writeByte(S2C_SPAWNWORM);
979 	bs.writeInt(Worm->getID(), 1);
980 	bs.writeInt( (int)pos.x, 2);
981 	bs.writeInt( (int)pos.y, 2);
982 
983 	SendPacket(&bs);
984 }
985 
SendWormDied(CWorm * Worm)986 void CServerNetEngine::SendWormDied(CWorm *Worm)
987 {
988 	CBytestream bs;
989 	bs.writeByte(S2C_WORMDOWN);
990 	bs.writeInt(Worm->getID(), 1);
991 
992 	SendPacket(&bs);
993 }
994 
SendWormScore(CWorm * Worm)995 void CServerNetEngine::SendWormScore(CWorm *Worm)
996 {
997 	CBytestream bs;
998 	bs.writeByte(S2C_SCOREUPDATE);
999 	bs.writeInt(Worm->getID(), 1);
1000 	bs.writeInt16(Worm->getLives());
1001 
1002 	// Overflow hack
1003 	if (Worm->getScore() > 255)
1004 		bs.writeInt(255, 1);
1005 	else
1006 		bs.writeInt(Worm->getScore() > 0 ? Worm->getScore() : 0, 1);
1007 
1008 	SendPacket(&bs);
1009 }
1010 
SendWormScore(CWorm * Worm)1011 void CServerNetEngineBeta9::SendWormScore(CWorm *Worm)
1012 {
1013 	// If we have some damage reports in buffer send them first so clients won't sum up updated damage score and reported damage packet sent later
1014 	SendReportDamage(true);
1015 
1016 	CBytestream bs;
1017 	bs.writeByte(S2C_SCOREUPDATE);
1018 	bs.writeInt(Worm->getID(), 1);
1019 	if (tLXOptions->tGameInfo.iLives < 0 && Worm->getLives() == WRM_UNLIM &&
1020 		cl->getClientVersion() >= OLXRcVersion(0,58,5) && cl->getClientVersion() < OLXBetaVersion(0,59,0)) {
1021 		bs.writeInt16(Worm->getDeaths()); // Send deaths count for matches with unlimited lives, 0.58_rc5 and up, and I'm too lazy to intoduce another CServerNetEngine class
1022 	} else {
1023 		bs.writeInt16(Worm->getLives());	// Still int16 to allow WRM_OUT parsing (maybe I'm wrong though)
1024 	}
1025 	bs.writeInt(Worm->getScore(), 4); // Negative kills are allowed
1026 	bs.writeFloat(Worm->getDamage());
1027 
1028 	SendPacket(&bs);
1029 }
1030 
QueueReportDamage(int victim,float damage,int offender)1031 void CServerNetEngineBeta9::QueueReportDamage(int victim, float damage, int offender)
1032 {
1033 	// Buffer up all damage and send it once per 0.1 second for LAN nettype, or once per 0.3 seconds for modem
1034 	std::pair< int, int > dmgPair = std::make_pair( victim, offender );
1035 	if( cDamageReport.count( dmgPair ) == 0 )
1036 		cDamageReport[ dmgPair ] = 0;
1037 	cDamageReport[ dmgPair ] += damage;
1038 
1039 	SendReportDamage();
1040 }
1041 
SendReportDamage(bool flush)1042 void CServerNetEngineBeta9::SendReportDamage(bool flush)
1043 {
1044 	if( ! flush && tLX->currentTime - fLastDamageReportSent < 0.1f * ( NST_LOCAL - cl->getNetSpeed() ) )
1045 		return;
1046 
1047 	CBytestream bs;
1048 
1049 	for( std::map< std::pair< int, int >, float > :: iterator it = cDamageReport.begin();
1050 			it != cDamageReport.end(); it++ )
1051 	{
1052 		int victim = it->first.first;
1053 		int offender = it->first.second;
1054 		float damage = it->second;
1055 		bs.writeByte(S2C_REPORTDAMAGE);
1056 		bs.writeByte(victim);
1057 		bs.writeFloat(damage);
1058 		bs.writeByte(offender);
1059 	}
1060 
1061 	SendPacket(&bs);
1062 
1063 	cDamageReport.clear();
1064 	fLastDamageReportSent = tLX->currentTime;
1065 }
1066 
SendTeamScoreUpdate()1067 void CServerNetEngineBeta9::SendTeamScoreUpdate() {
1068 	// only do this in a team game
1069 	if(server->getGameMode()->GeneralGameType() != GMT_TEAMS) return;
1070 
1071 	CBytestream bs;
1072 	bs.writeByte(S2C_TEAMSCOREUPDATE);
1073 	bs.writeByte(server->getGameMode()->GameTeams());
1074 	for(int i = 0; i < server->getGameMode()->GameTeams(); ++i) {
1075 		bs.writeInt16(server->getGameMode()->TeamScores(i));
1076 	}
1077 	SendPacket(&bs);
1078 }
1079 
SendTeamScoreUpdate()1080 void GameServer::SendTeamScoreUpdate() {
1081 	for(int c = 0; c < MAX_CLIENTS; c++) {
1082 		if(cClients[c].getStatus() == NET_CONNECTED)
1083 			cClients[c].getNetEngine()->SendTeamScoreUpdate();
1084 	}
1085 }
1086 
SendWormProperties(bool onlyIfNotDef)1087 void CServerNetEngine::SendWormProperties(bool onlyIfNotDef) {
1088 	CWorm* w = server->getWorms();
1089 	for(int i = 0; i < MAX_WORMS; ++i, ++w) {
1090 		if(!w->isUsed()) continue;
1091 		if(onlyIfNotDef && isWormPropertyDefault(w)) continue;
1092 
1093 		SendWormProperties(w);
1094 	}
1095 }
1096 
SendWormProperties(CWorm * worm)1097 void CServerNetEngine::SendWormProperties(CWorm* worm) {
1098 	if(!worm->isUsed()) {
1099 		warnings << "SendWormProperties called for unused worm" << endl;
1100 		return;
1101 	}
1102 
1103 	if(isWormPropertyDefault(worm)) return; // ok, don't give a warning in that case
1104 
1105 	warnings << "SendWormProperties cannot be used for clients with <Beta9 (" << cl->debugName() << ")" << endl;
1106 }
1107 
SendWormProperties(CWorm * worm)1108 void CServerNetEngineBeta9::SendWormProperties(CWorm* worm) {
1109 	if(!worm->isUsed()) {
1110 		warnings << "SendWormProperties called for unused worm" << endl;
1111 		return;
1112 	}
1113 
1114 	CBytestream bs;
1115 	bs.writeByte(S2C_SETWORMPROPS);
1116 	bs.writeByte(worm->getID());
1117 	bs.writeBit(worm->canUseNinja());
1118 	bs.writeBit(worm->canAirJump());
1119 	bs.writeFloat(worm->speedFactor());
1120 	bs.writeFloat(worm->damageFactor());
1121 	bs.writeFloat(worm->shieldFactor());
1122 	SendPacket(&bs);
1123 }
1124 
isWormPropertyDefault(CWorm * worm)1125 bool CServerNetEngine::isWormPropertyDefault(CWorm* worm) {
1126 	// defaults are set in CWorm::Prepare
1127 	return
1128 		worm->speedFactor() == 1.0f &&
1129 		worm->damageFactor() == 1.0f &&
1130 		worm->shieldFactor() == 1.0f &&
1131 		worm->canUseNinja() &&
1132 		!worm->canAirJump();
1133 }
1134 
SendSelectWeapons(CWorm * worm)1135 void CServerNetEngine::SendSelectWeapons(CWorm* worm) {
1136 	warnings << "SendSelectWeapons not supported for " << cl->debugName() << endl;
1137 }
1138 
SendSelectWeapons(CWorm * worm)1139 void CServerNetEngineBeta9::SendSelectWeapons(CWorm* worm) {
1140 	if(!worm->isUsed()) {
1141 		warnings << "SendSelectWeapons called for unused worm" << endl;
1142 		return;
1143 	}
1144 
1145 	CBytestream bs;
1146 	bs.writeByte(S2C_SELECTWEAPONS);
1147 	bs.writeByte(worm->getID());
1148 	SendPacket(&bs);
1149 }
1150 
SetWormSpeedFactor(int wormID,float f)1151 void GameServer::SetWormSpeedFactor(int wormID, float f) {
1152 	if(wormID < 0 || wormID >= MAX_WORMS || !cWorms[wormID].isUsed()) {
1153 		warnings << "SetWormSpeedFactor: worm " << wormID << " is invalid" << endl;
1154 		return;
1155 	}
1156 
1157 	if(cWorms[wormID].speedFactor() == f) return; // nothing need to be changed
1158 
1159 	cWorms[wormID].setSpeedFactor(f);
1160 
1161 	for(int c = 0; c < MAX_CLIENTS; c++) {
1162 		if(cClients[c].getStatus() == NET_CONNECTED)
1163 			cClients[c].getNetEngine()->SendWormProperties(&cWorms[wormID]);
1164 	}
1165 }
1166 
SetWormCanUseNinja(int wormID,bool b)1167 void GameServer::SetWormCanUseNinja(int wormID, bool b) {
1168 	if(wormID < 0 || wormID >= MAX_WORMS || !cWorms[wormID].isUsed()) {
1169 		warnings << "SetWormCanUseNinja: worm " << wormID << " is invalid" << endl;
1170 		return;
1171 	}
1172 
1173 	if(cWorms[wormID].canUseNinja() == b) return; // nothing need to be changed
1174 
1175 	cWorms[wormID].setCanUseNinja(b);
1176 
1177 	for(int c = 0; c < MAX_CLIENTS; c++) {
1178 		if(cClients[c].getStatus() == NET_CONNECTED)
1179 			cClients[c].getNetEngine()->SendWormProperties(&cWorms[wormID]);
1180 	}
1181 }
1182 
SetWormDamageFactor(int wormID,float f)1183 void GameServer::SetWormDamageFactor(int wormID, float f) {
1184 	if(wormID < 0 || wormID >= MAX_WORMS || !cWorms[wormID].isUsed()) {
1185 		warnings << "SetWormDamageFactor: worm " << wormID << " is invalid" << endl;
1186 		return;
1187 	}
1188 
1189 	if(cWorms[wormID].damageFactor() == f) return; // nothing need to be changed
1190 
1191 	cWorms[wormID].setDamageFactor(f);
1192 
1193 	for(int c = 0; c < MAX_CLIENTS; c++) {
1194 		if(cClients[c].getStatus() == NET_CONNECTED)
1195 			cClients[c].getNetEngine()->SendWormProperties(&cWorms[wormID]);
1196 	}
1197 }
1198 
SetWormShieldFactor(int wormID,float f)1199 void GameServer::SetWormShieldFactor(int wormID, float f) {
1200 	if(wormID < 0 || wormID >= MAX_WORMS || !cWorms[wormID].isUsed()) {
1201 		warnings << "SetWormDamageFactor: worm " << wormID << " is invalid" << endl;
1202 		return;
1203 	}
1204 
1205 	if(cWorms[wormID].shieldFactor() == f) return; // nothing need to be changed
1206 
1207 	cWorms[wormID].setShieldFactor(f);
1208 
1209 	for(int c = 0; c < MAX_CLIENTS; c++) {
1210 		if(cClients[c].getStatus() == NET_CONNECTED)
1211 			cClients[c].getNetEngine()->SendWormProperties(&cWorms[wormID]);
1212 	}
1213 }
1214 
SetWormCanAirJump(int wormID,bool b)1215 void GameServer::SetWormCanAirJump(int wormID, bool b) {
1216 	if(wormID < 0 || wormID >= MAX_WORMS || !cWorms[wormID].isUsed()) {
1217 		warnings << "SetWormCanAirJump: worm " << wormID << " is invalid" << endl;
1218 		return;
1219 	}
1220 
1221 	if(cWorms[wormID].canAirJump() == b) return; // nothing need to be changed
1222 
1223 	cWorms[wormID].setCanAirJump(b);
1224 
1225 	for(int c = 0; c < MAX_CLIENTS; c++) {
1226 		if(cClients[c].getStatus() == NET_CONNECTED)
1227 			cClients[c].getNetEngine()->SendWormProperties(&cWorms[wormID]);
1228 	}
1229 }
1230