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 // Client class - Sending routines
13 // Created 22/7/02
14 // Jason Boettcher
15 
16 #include "LieroX.h"
17 
18 #include "CClientNetEngine.h"
19 #include "StringUtils.h"
20 #include "CClient.h"
21 #include "Protocol.h"
22 #include "CWorm.h"
23 #include "MathLib.h"
24 #include "ChatCommand.h"
25 #include "Command.h"
26 #include "EndianSwap.h"
27 #include "CServer.h"
28 #include "AuxLib.h"
29 #include "CChannel.h"
30 #include "DeprecatedGUI/Menu.h"
31 #include "IRC.h"
32 #include "console.h"
33 
34 
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 the worm details
SendWormDetails()42 void CClientNetEngine::SendWormDetails()
43 {
44 	// Don't flood packets so often
45 	// we are checking in w->checkPacketNeeded() if we need to send an update
46 	// we are checking with bandwidth if we should add an update
47 	/*if ((tLX->currentTime - fLastUpdateSent) <= tLXOptions->fUpdatePeriod)
48 		if (tGameInfo.iGameType != GME_LOCAL)
49 			return; */
50 
51 	CBytestream bs;
52 	uint i;
53 
54 	// If all my worms are dead, don't send
55 	bool Alive = false;
56 	for(i=0;i<client->iNumWorms;i++) {
57 		if(client->cLocalWorms[i] && client->cLocalWorms[i]->getAlive()) {
58 			Alive = true;
59 			break;
60 		}
61 	}
62 
63 	if(!Alive)
64 		return;
65 
66 
67 	// Check if we need to write the state update
68 	bool update = false;
69 	for(i = 0; i < client->iNumWorms; i++)
70 		if (client->cLocalWorms[i] && client->cLocalWorms[i]->checkPacketNeeded())  {
71 			update = true;
72 			break;
73 		}
74 
75 	// No update, just quit
76 	if (!update)
77 		return;
78 
79 	// TODO: Have we always used the limitcheck from GameServer here?
80 	// We should perhaps move it out from GameServer. Looks a bit strange here to use something from GameServer.
81 	if(	tLX->iGameType == GME_JOIN // we are a client in a netgame
82 	&& !GameServer::checkUploadBandwidth(client->getChannel()->getOutgoingRate()) )
83 		return;
84 
85 	client->fLastUpdateSent = tLX->currentTime;
86 
87 	// Write the update
88 	bs.writeByte(C2S_UPDATE);
89 
90 	for(i = 0; i < client->iNumWorms; i++)
91 		if(client->cLocalWorms[i])
92 			client->cLocalWorms[i]->writePacket(&bs, false, NULL);
93 
94 	client->bsUnreliable.Append(&bs);
95 }
96 
97 
SendGameReady()98 void CClientNetEngine::SendGameReady()
99 {
100 	CBytestream bs;
101 	bs.writeByte(C2S_IMREADY);
102 
103 	if(client->serverChoosesWeapons()) {
104 		bs.writeByte(0);
105 	} else {
106 		std::list<CWorm*> worms;
107 		for(unsigned int i=0;i<client->iNumWorms;i++)
108 			if(client->cLocalWorms[i]) worms.push_back(client->cLocalWorms[i]);
109 
110 		// Send my worm's weapon details
111 		bs.writeByte(worms.size());
112 		for(std::list<CWorm*>::iterator i = worms.begin(); i != worms.end(); ++i)
113 			(*i)->writeWeapons( &bs );
114 	}
115 
116 	client->cNetChan->AddReliablePacketToSend(bs);
117 }
118 
119 
120 ///////////////////
121 // Send a death
SendDeath(int victim,int killer)122 void CClientNetEngine::SendDeath(int victim, int killer)
123 {
124 	CBytestream bs;
125 
126 	bs.writeByte(C2S_DEATH);
127 	bs.writeInt(victim,1);
128 	bs.writeInt(killer,1);
129 
130 	client->cNetChan->AddReliablePacketToSend(bs);
131 }
132 
SendDeath(int victim,int killer)133 void CClientNetEngineBeta9::SendDeath(int victim, int killer)
134 {
135 	// 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
136 	SendReportDamage(true);
137 
138 	CClientNetEngine::SendDeath(victim, killer);
139 }
140 
141 
142 ///////////////////
143 // Send a string of text
SendText(const std::string & sText,std::string sWormName)144 void CClientNetEngine::SendText(const std::string& sText, std::string sWormName)
145 {
146 	if(HandleDebugCommand(sText)) return;
147 
148 	bool chat_command = sText.size() >= 2 && sText[0] == '/' && sText[1] != '/';
149 
150 	if (sText.find("/irc ") == 0 || sText.find("/chat ") == 0)  { // Send text to IRC
151 		bool res = false;
152 		if (GetGlobalIRC())
153 			res = GetGlobalIRC()->sendChat(sText.substr(sText.find(' ') + 1));
154 		if (!res)
155 			client->cChatbox.AddText("Could not send the IRC message", tLX->clNetworkText, TXT_NETWORK, tLX->currentTime);
156 		return;
157 	}
158 
159 	// We can safely send long messages to servers >= beta8
160 	if (client->getServerVersion() >= OLXBetaVersion(8))  {
161 
162 		// HTML support since beta 8
163 		SendTextInternal(OldLxCompatibleString(sText), sWormName);
164 
165 
166 	// <=beta7 server
167 	} else {
168 
169 		if (chat_command &&
170 			client->getServerVersion() < OLXBetaVersion(3) && // <beta3 clients don't have chat command support
171 			sText.find("/me ") != 0) // Ignores "/me" special command
172 		{
173 			// Try if we can execute the same command in console (mainly for "/suicide" command to work on all servers)
174 			if( !subStrCaseEqual( sText.substr(1), "suicide", 7 ) )
175 			    Con_Execute(sText.substr(1));
176 			else
177 				client->cChatbox.AddText("HINT: server cannot execute commands, only OLX beta3+ can", tLX->clNotice, TXT_NOTICE, tLX->currentTime);
178 
179 			return;
180 
181 		} else if (chat_command &&
182 				   client->getServerVersion() >= OLXBetaVersion(3)) {
183 			// we don't have to split chat commands
184 			// "/me ..." is also save, because server uses SendGlobalText for sending and it splits the text there
185 			SendTextInternal(OldLxCompatibleString(sText), sWormName);
186 
187 		} else { // we can savely split the message (and we have to)
188 
189 			// If the text is too long, split it in smaller pieces and then send (backward comaptibility)
190 			int name_w = tLX->cFont.GetWidth(sWormName + ": ");
191 			std::vector<std::string> split = splitstring(StripHtmlTags(sText), 63,
192 				client->iNetStatus == NET_CONNECTED ? 600 - name_w : 300 - name_w, tLX->cFont);
193 
194 			// Check
195 			if (split.size() == 0)  {  // More than weird...
196 				SendTextInternal(OldLxCompatibleString(StripHtmlTags(sText)), sWormName);
197 				return;
198 			}
199 
200 			// Send the first chunk
201 			SendTextInternal(OldLxCompatibleString(split[0]), sWormName);
202 
203 			// Send the text
204 			for (std::vector<std::string>::const_iterator it = split.begin() + 1; it != split.end(); it++)
205 				SendTextInternal(OldLxCompatibleString(*it), "");
206 		}
207 	}
208 }
209 
SendChatCommandCompletionRequest(const std::string & startStr)210 void CClientNetEngineBeta7::SendChatCommandCompletionRequest(const std::string& startStr) {
211 	CBytestream bs;
212 	bs.writeByte(C2S_CHATCMDCOMPLREQ);
213 	bs.writeString(startStr);
214 
215 	client->cNetChan->AddReliablePacketToSend(bs);
216 }
217 
SendAFK(int wormid,AFK_TYPE afkType,const std::string & message)218 void CClientNetEngineBeta7::SendAFK(int wormid, AFK_TYPE afkType, const std::string & message ) {
219 	if( client->getServerVersion() < OLXBetaVersion(7) )
220 		return;
221 
222 	std::string msg = message;
223 	if( msg == "" )
224 	{
225 		switch(afkType) {
226 			case AFK_BACK_ONLINE: break;
227 			case AFK_TYPING_CHAT: msg = "(typing)"; break;
228 			case AFK_AWAY: msg = "(away)"; break;
229 			case AFK_SELECTING_WPNS: msg = "(selecting weapons)"; break;
230 			case AFK_CONSOLE: msg = "(console)"; break;
231 			case AFK_MENU: msg = "(menu)"; break;
232 		}
233 	}
234 
235 
236 	for( int i=0; i < client->getNumWorms(); i++ )
237 		if(	client->getWorm(i)->getID() == wormid )
238 			client->getWorm(i)->setAFK(afkType, msg);
239 
240 	CBytestream bs;
241 	bs.writeByte(C2S_AFK);
242 	bs.writeByte(wormid);
243 	bs.writeByte(afkType);
244 	if(msg.size() > 127)
245 		msg = msg.substr(0, 127);
246 	bs.writeString(msg);
247 
248 	client->cNetChan->AddReliablePacketToSend(bs);
249 }
250 
251 /////////////////////
252 // Internal function for text sending, does not do any checks or parsing
SendTextInternal(const std::string & sText,const std::string & sWormName)253 void CClientNetEngine::SendTextInternal(const std::string& sText, const std::string& sWormName)
254 {
255 	if(!client) {
256 		errors << "CClientNetEngine::SendTextInternal(" << sWormName << ": " << sText << "): my client is unset" << endl;
257 		return;
258 	}
259 
260 	if(!client->cNetChan) {
261 		errors << "CClientNetEngine::SendTextInternal(" << sWormName << ": " << sText << "): cNetChan of my client (" << client->debugName() << ") is unset" << endl;
262 		return;
263 	}
264 
265 	CBytestream bs;
266 	bs.writeByte(C2S_CHATTEXT);
267 	if (sWormName.size() == 0)
268 		bs.writeString(sText);
269 	else if( sText.find("/me ") == 0 && client->getServerVersion() < OLXBetaVersion(0,58,1) )	// "/me " chat command
270 		bs.writeString( "* " + sWormName + " " + sText.substr(4)); // Add star so clients with empty name won't fake others
271 	else	// "/me " command is server-sided on Beta9
272 		bs.writeString(sWormName + ": " + sText);
273 
274 	client->cNetChan->AddReliablePacketToSend(bs);
275 }
276 
277 #ifdef FUZZY_ERROR_TESTING
278 //////////////////
279 // Send a random packet to server (used for debugging)
SendRandomPacket()280 void CClientNetEngine::SendRandomPacket()
281 {
282 	// don't send random packets from the local client to our own server
283 	if( tGameInfo.iGameType != GME_JOIN ) return;
284 
285 	CBytestream bs;
286 
287 	int random_length = GetRandomInt(50);
288 	for (int i=0; i < random_length; i++)
289 		bs.writeByte((uchar)GetRandomInt(255));
290 
291 	client->cNetChan->AddReliablePacketToSend(bs);
292 
293 	bs.Clear();
294 
295 	// Test connectionless packets
296 	if (GetRandomInt(100) >= 75)  {
297 		bs.writeInt(-1, 4);
298 		static const std::string commands[] = { "lx::getchallenge", "lx::connect", "lx::ping", "lx::query",
299 			"lx::getinfo", "lx::wantsjoin", "lx::traverse", "lx::registered"};
300 		bs.writeString(commands[GetRandomInt(500) % (sizeof(commands)/sizeof(std::string))]);
301 		for (int i=0; i < random_length; i++)
302 			bs.writeByte((uchar)GetRandomInt(255));
303 		SetRemoteNetAddr(client->tSocket, client->cNetChan->getAddress());
304 		bs.Send(client->tSocket);
305 	}
306 }
307 #endif
308 
SendGrabBonus(int id,int wormid)309 void CClientNetEngine::SendGrabBonus(int id, int wormid)
310 {
311 	CBytestream bs;
312 	bs.writeByte(C2S_GRABBONUS);
313 	bs.writeByte(id);
314 	bs.writeByte(wormid);
315 	bs.writeByte(client->cRemoteWorms[wormid].getCurrentWeapon());
316 
317 	client->cNetChan->AddReliablePacketToSend(bs);
318 }
319 
SendUpdateLobby(bool ready)320 void CClientNetEngine::SendUpdateLobby(bool ready)
321 {
322 	CBytestream bs;
323 	bs.writeByte(C2S_UPDATELOBBY);
324 	bs.writeByte(ready);
325 	client->getChannel()->AddReliablePacketToSend(bs);
326 }
327 
SendDisconnect()328 void CClientNetEngine::SendDisconnect()
329 {
330 	CBytestream bs;
331 
332 	bs.writeByte(C2S_DISCONNECT);
333 
334 	// Send the pack a few times to make sure the server gets the packet
335 	if( client->cNetChan != NULL)
336 		for(int i=0;i<3;i++)
337 			client->cNetChan->Transmit(&bs);
338 }
339 
SendFileData()340 void CClientNetEngine::SendFileData()
341 {
342 	CBytestream bs;
343 	bs.writeByte(C2S_SENDFILE);
344 	client->getUdpFileDownloader()->send(&bs);
345 	client->getChannel()->AddReliablePacketToSend(bs);
346 }
347 
QueueReportDamage(int victim,float damage,int offender)348 void CClientNetEngineBeta9::QueueReportDamage(int victim, float damage, int offender)
349 {
350 	// Buffer up all damage and send it once per 0.1 second for LAN nettype, or once per 0.3 seconds for modem
351 	std::pair< int, int > dmgPair = std::make_pair( victim, offender );
352 	if( cDamageReport.count( dmgPair ) == 0 )
353 		cDamageReport[ dmgPair ] = 0;
354 	cDamageReport[ dmgPair ] += damage;
355 
356 	SendReportDamage();
357 }
358 
SendReportDamage(bool flush)359 void CClientNetEngineBeta9::SendReportDamage(bool flush)
360 {
361 	if( ! flush && tLX->currentTime - fLastDamageReportSent < 0.1f * ( NST_LOCAL - client->getNetSpeed() ) )
362 		return;
363 
364 	CBytestream bs;
365 
366 	for( std::map< std::pair< int, int >, float > :: iterator it = cDamageReport.begin();
367 			it != cDamageReport.end(); it++ )
368 	{
369 		int victim = it->first.first;
370 		int offender = it->first.second;
371 		float damage = it->second;
372 		bs.writeByte(C2S_REPORTDAMAGE);
373 		bs.writeByte(victim);
374 		bs.writeFloat(damage);
375 		bs.writeByte(offender);
376 	}
377 
378 	client->cNetChan->AddReliablePacketToSend(bs);
379 
380 	cDamageReport.clear();
381 	fLastDamageReportSent = tLX->currentTime;
382 }
383